Lleva los clips renderizados a TikTok, Instagram, YouTube y más
Quickstart: tu primer clip
Siete peticiones separan un enlace de YouTube de un clip vertical renderizado. Esta guía recorre las siete, en orden, con la respuesta de cada una.
Antes de empezar, genera una clave de API y expórtala en tu terminal:
export CUTPRO_API_KEY="tu_clave"1. Analiza el vídeo
Sección titulada «1. Analiza el vídeo»POST /clips/info lee los metadatos del enlace y calcula el coste. Analizar no consume créditos.
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": "Entrevista completa: 2 horas sobre carrera", "author": "Canal de ejemplo", "platform": "youtube", "duration": 7245, "credits_cost": 121, "current_balance": 480, "credits_unlimited": false, "force_watermark": false}Guarda el video_id: es lo que identifica el vídeo en todas las llamadas siguientes.
2. Envíalo al recorte
Sección titulada «2. Envíalo al recorte»POST /clips crea el envío. Los créditos se cobran aquí, por el importe que mostró el paso anterior.
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}El timeframe es opcional: omítelo para procesar el vídeo entero, o redúcelo a un tramo (en segundos) para gastar menos. Arriba, la primera media hora costó 30 créditos en vez de 121.
3. Consulta hasta que termine
Sección titulada «3. Consulta hasta que termine»Haz polling del envío cada 10 o 15 segundos. El status pasa por queued, downloading, transcribing, video_analysis, analyzing y finalizing hasta llegar a completed (o 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}Mientras el vídeo espera en la cola, queue_position y estimated_time (en segundos) dicen cuánto falta. Cuando el status pasa a completed, clips_count trae cuántos clips generó la IA.
4. Recupera los clips generados
Sección titulada «4. Recupera los clips generados»curl https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/clips \ -H "X-Api-Key: $CUTPRO_API_KEY"{ "clips": [ { "id": "7412908412887301", "title": "El error que le costó su primera empresa", "rating": 9.2, "start_time": 412.5, "end_time": 461.8, "language": "es", "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 }}El rating es la nota de la IA de 0 a 10: cuanto más alta, más potencial tiene el corte. Ordena por ella para quedarte con los mejores primero.
5. Renderiza un clip
Sección titulada «5. Renderiza un clip»POST .../render genera el MP4 final. Para aplicar antes uno de tus estilos, llama a POST .../apply_template en el envío.
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}Cuando from_cache llega como true, la respuesta es un 200 con el download_url ya rellenado: ese clip ya se había renderizado con la misma configuración y puedes saltarte los dos pasos siguientes.
6. Consulta el render
Sección titulada «6. Consulta el 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"}El status pasa por queued, active y completed, y progress va de 0 a 100.
7. Descarga el MP4
Sección titulada «7. Descarga el 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": "el-error-que-le-costo-su-primera-empresa.mp4"}La url está firmada y vale una hora. Descarga el archivo dentro de ese plazo o pide otra.
El flujo entero en un archivo
Sección titulada «El flujo entero en un archivo»Los siete pasos encadenados, con el polling ya resuelto. Cambia la URL del vídeo y ejecútalo.
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} créditos`);
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']} créditos")
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)¿Y ahora?
Sección titulada «¿Y ahora?»Cómo se consumen los créditos y cómo consultar el saldo