# Cut.Pro > Documentação da API do Cut.Pro: clipagem com IA, renderização e publicação, de forma programática. # Autenticação > Como autenticar suas requisições e selecionar o workspace Toda requisição à API precisa de uma **chave de API**. ## Gerando sua chave Acesse [Chaves de API](https://cut.pro/studio/me/api-keys) nas configurações da sua conta e clique em **Nova chave**. Guarde a chave em local seguro: ela não é exibida de novo depois de criada. > A chave dá acesso aos créditos e às contas conectadas do workspace. Mantenha-a no servidor, em variável de ambiente, nunca no código do navegador ou do app. ## Enviando a chave Envie a chave em **todas** as requisições, de uma das duas formas: ```bash title="Header X-Api-Key" curl https://api.cut.pro/api/v1/workspace \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```bash title="Authorization Bearer" curl https://api.cut.pro/api/v1/workspace \ -H "Authorization: Bearer $CUTPRO_API_KEY" ``` Chave ausente, revogada ou inválida volta assim: ```json title="Resposta 401" { "code": "UNAUTHORIZED", "message": "Unauthorized" } ``` ## Workspaces Uma chave de API é vinculada a **um ou mais workspaces** (tokens com escopo, no estilo Cloudflare). Créditos, submissões, clipes e renders sempre pertencem ao workspace que a requisição resolver. **Chave de um workspace** Toda requisição opera nesse workspace automaticamente. **Nenhum header extra é necessário.** **Chave multi-workspace** Envie o header `X-Workspace-Id` em cada requisição para escolher qual workspace a chamada atinge: ```bash curl https://api.cut.pro/api/v1/balance \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "X-Workspace-Id: 7401220118845503" ``` Sem o header, a resposta é `400 MISSING_WORKSPACE_ID`, trazendo a lista de workspaces permitidos. > Chame `GET /workspace` para inspecionar qual workspace a requisição atual resolveu, junto com o plano e o seu papel nele. ## Conexões sociais As contas sociais conectadas (TikTok, Instagram, YouTube e as demais) são os IDs que você passa como `connectionId` ao criar um post. O ciclo de vida das conexões **não é exposto pela API**: para conectar uma nova conta, entre em [cut.pro](https://cut.pro) e use o fluxo de OAuth dentro do app. --- # Enviando um vídeo para clipagem > As duas formas de obter um video_id: por URL pública ou enviando seu próprio arquivo Antes de gerar clipes, você precisa de um `video_id`. Há duas formas de obter um, e ambas convergem para o mesmo passo seguinte: chamar `POST /clips` com esse `video_id` (veja o [Quickstart](/docs/api-reference/quickstart)). > O vídeo precisa ter **no mínimo 1 minuto**. A duração máxima depende do seu plano. ## Opção A: por URL pública A forma mais simples. Envie a URL de um vídeo público (YouTube, TikTok, Twitch, Kick, Vimeo, etc.) para análise. **Analisar não custa créditos.** ```bash 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" }' ``` A resposta traz `video_id`, `credits_cost` (o que será cobrado ao submeter), `current_balance` e o título e a duração do vídeo. Guarde o `video_id` e siga para `POST /clips`. ### Quando o link não serve Nem todo link público pode ser clipado. Estes casos voltam com `422` e um `code` que diz o motivo: | `code` | O que aconteceu | |---|---| | `LIVE_STREAM` | A transmissão ainda está no ar. Espere ela terminar e virar vídeo | | `PLAYLIST_URL` | A URL é de uma playlist. Envie o link de um vídeo | | `CHANNEL_URL` | A URL é de um canal. Envie o link de um vídeo | | `SHORTS_NOT_SUPPORTED` | O vídeo já é um curto vertical, não há o que cortar | | `AUDIO_ONLY` | O link não tem faixa de vídeo | | `INVALID_DURATION` | Menos de 1 minuto, ou mais do que o seu plano permite | | `INVALID_URL` | A plataforma não é suportada ou o endereço está errado | Vídeo privado, com restrição de idade ou bloqueado por região volta com `403` (`PRIVATE_VIDEO`, `AGE_RESTRICTED`, `REGION_BLOCKED`). ## Opção B: enviando seu próprio arquivo Para vídeos do seu computador, o envio é feito em **três passos** com uma URL pré-assinada (presigned), enviando os bytes direto para o armazenamento. > Limites do upload: **máximo 2 GB** e extensões **`.mp4`, `.mov`, `.webm`, `.mkv`**. A URL pré-assinada expira em **1 hora**. 1. ### Inicie o upload Declare o nome do arquivo e o tipo de conteúdo. A resposta traz o `video_id`, a `upload_url` (para onde enviar os bytes) e `expires_in` (segundos até expirar). ```bash curl -X POST https://api.cut.pro/api/v1/videos/upload \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "file_name": "minha-live.mp4", "content_type": "video/mp4" }' ``` 2. ### Envie os bytes para a upload_url Faça um `PUT` do arquivo direto na `upload_url`. **Não envie a sua chave de API aqui** (a URL já é assinada), e use o **mesmo `Content-Type`** que você declarou no passo anterior. ```bash curl -X PUT "UPLOAD_URL" \ -H "Content-Type: video/mp4" \ --data-binary @minha-live.mp4 ``` O `PUT` deve retornar `200` antes de seguir. 3. ### Finalize o upload Registre o upload informando as dimensões e a duração do arquivo (medidas no seu lado). A resposta traz a metadata do vídeo e o `credits_cost`, igual à análise por URL. ```bash curl -X POST https://api.cut.pro/api/v1/videos/upload/complete \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_id": "VIDEO_ID", "file_name": "minha-live.mp4", "duration": 5400, "width": 1920, "height": 1080 }' ``` Chame este passo apenas depois que o `PUT` retornar `200`. Se o arquivo não estiver no armazenamento, a resposta é `404 FILE_NOT_FOUND`. Se a duração estiver fora dos limites, retorna `400 VIDEO_TOO_SHORT` ou `VIDEO_TOO_LONG` (com `max_duration`). ## Próximo passo - [Continue no Quickstart](https://cut.pro/docs/api-reference/quickstart): Com o `video_id` em mãos, siga a partir do passo "Envie para clipagem" (`POST /clips`) --- # Visão geral da API > Automatize a clipagem e renderização de vídeos com IA, de forma programática A **API da CutPro** automatiza todo o fluxo de clipagem com IA: você envia o link de um vídeo, a IA encontra os melhores momentos, e você recebe os clipes prontos para renderizar e publicar, tudo por requisições HTTP. ## URL base Todas as rotas usam o prefixo: ``` https://api.cut.pro/api/v1 ``` A autenticação é por chave no header `X-Api-Key`, e o acesso à API exige o plano Pro. Veja [Autenticação](/docs/api-reference/autenticacao). ## O fluxo típico A API segue o mesmo caminho do estúdio, em etapas que você controla por requisição: 1. **Analisar (sem custo).** Pré-visualize metadados e o custo em créditos de um link público, sem ser cobrado. `POST /clips/info` 2. **Enviar para clipagem.** Submeta o vídeo. Os créditos são debitados na hora. `POST /clips` 3. **Acompanhar a submissão.** Faça polling até o `status` virar `completed` ou `failed`. `GET /clips/{videoId}/submissions/{submissionId}` 4. **Buscar os clipes gerados.** Pegue os clipes que a IA produziu, com timestamps, nota e URLs. `GET /clips/{videoId}/submissions/{submissionId}/clips` 5. **Aplicar um template (opcional).** Aplique um dos seus templates em lote aos clipes. `POST /clips/{videoId}/submissions/{submissionId}/apply_template` 6. **Renderizar cada clipe.** Gere o MP4 final e faça polling até concluir. `POST .../clips/{clipId}/render` e `GET /renders/{renderId}` 7. **Baixar e publicar.** Baixe o vídeo renderizado e, se quiser, publique nas redes. `GET /renders/{renderId}/download` e `POST /posts` O [Quickstart](/docs/api-reference/quickstart) percorre esses sete passos com requisições reais e a resposta de cada uma. ## Erros Toda resposta de erro tem o mesmo formato: um `code` estável, em maiúsculas, que o seu código trata num `switch`. O texto que a pessoa lê é escrito por você, no idioma dela. ```json title="Resposta 402" { "code": "INSUFFICIENT_CREDITS", "extra": { "credits_needed": 121, "current_balance": 40, "is_reclip": false, "scope": "workspace", "is_owner": true } } ``` | Status | Quando acontece | Exemplos de `code` | |---|---|---| | `400` | Corpo inválido ou operação fora de ordem | `VALIDATION_ERROR`, `TIMEFRAME_OUT_OF_BOUNDS`, `INVALID_METADATA` | | `401` | Chave ausente, revogada ou inválida | `UNAUTHORIZED` | | `402` | Créditos insuficientes para a submissão | `INSUFFICIENT_CREDITS` | | `403` | O vídeo ou o recurso não está acessível | `PRIVATE_VIDEO`, `REGION_BLOCKED`, `DAILY_LIMIT_EXCEEDED` | | `404` | O recurso não existe nesse workspace | `VIDEO_NOT_FOUND`, `RENDER_NOT_FOUND` | | `409` | O recurso está num estado que não aceita a operação | `VIDEO_ALREADY_PROCESSING`, `DUPLICATE_POST` | | `410` | O resultado existiu e expirou | `SUBMISSION_EXPIRED`, `RENDER_FILE_EXPIRED` | | `422` | O link é válido mas o vídeo não serve para clipagem | `LIVE_STREAM`, `PLAYLIST_URL`, `AUDIO_ONLY` | | `429` | Limite de requisições atingido | `RATE_LIMIT_EXCEEDED` | Quando o `code` é `VALIDATION_ERROR`, a resposta traz também um `errors` com o campo que reprovou. Os códigos possíveis de cada rota estão listados na [referência](/docs/reference), resposta por resposta. ## Qualidade e direitos > A IA escolhe os momentos pelo que é dito, não pela imagem: vídeo com áudio limpo rende clipe melhor. Use apenas conteúdo que você tem permissão para editar. O CutPro não remove marca d'água de terceiros. ## Próximos passos - [Autenticação](https://cut.pro/docs/api-reference/autenticacao): Como gerar sua chave e enviá-la nas requisições, incluindo workspaces - [Quickstart](https://cut.pro/docs/api-reference/quickstart): Um exemplo completo, do link ao MP4 renderizado - [Workspace e créditos](https://cut.pro/docs/api-reference/saldo): Como o consumo de créditos funciona em cada submissão - [Publicação](https://cut.pro/docs/api-reference/postagem): Como publicar os clipes renderizados nas redes sociais --- # Conectar via MCP > Use o CutPro dentro do Claude, ChatGPT e outras ferramentas de IA via Model Context Protocol O **MCP (Model Context Protocol)** é um padrão aberto que conecta ferramentas de IA a serviços externos. Conectado ao CutPro, o modelo executa o fluxo inteiro sozinho: analisa o vídeo, clipa, renderiza e devolve o link. > O acesso à API exige o plano **Pro**. Gere uma chave em [Chaves de API](https://cut.pro/studio/me/api-keys) e use-a como `CUTPRO_API_KEY`. ## Local: Claude Code, Cursor, VS Code O servidor roda na sua máquina via `npx` e expõe os endpoints da API v1 como ferramentas, cobrindo workspace, saldo, vídeos e uploads, clipagem, clipes, templates, renders, publicação e conexões. **Claude Code** ```bash claude mcp add cutpro \ --env CUTPRO_API_KEY=$CUTPRO_API_KEY \ -- npx -y @cutpro/mcp ``` **Claude Desktop, Cursor, VS Code** ```json title="Configuração de servidores MCP" { "mcpServers": { "cutpro": { "command": "npx", "args": ["-y", "@cutpro/mcp"], "env": { "CUTPRO_API_KEY": "sua_chave" } } } } ``` > Chave multi-workspace? Adicione também `CUTPRO_WORKSPACE_ID` ao `env`. ### Exemplo de uso Depois de conectado, basta pedir em linguagem natural: > "Analise este vídeo do YouTube, gere os clipes, e me mande os 3 de maior nota já renderizados." A IA encadeia as ferramentas sozinha: `analyze_video`, `submit_clipping`, `get_submission` (até concluir), `list_clips`, `render_clip`, `get_render` e `get_render_download`. ## Remoto: ChatGPT e Claude.ai O mesmo servidor roda em `https://mcp.cut.pro` com **OAuth 2.1** completo (descoberta de metadados, registro dinâmico de cliente, PKCE e refresh token). É o caminho para os clientes que rodam na nuvem e não executam `npx`. 1. ### Adicione o conector No ChatGPT (Connectors) ou no Claude.ai (Settings, Connectors, Add custom connector), informe a URL `https://mcp.cut.pro`. 2. ### Autorize com sua chave O cliente abre uma página de consentimento. Cole sua [chave de API](https://cut.pro/studio/me/api-keys): ela é validada e o acesso é autorizado. 3. ### Pronto O cliente passa a usar as ferramentas do CutPro nas conversas. ## Deixe a IA ler esta documentação Para perguntas sobre o produto, sem executar nada, a documentação é publicada em texto puro no formato que os modelos leem direto: | Endereço | O que traz | |---|---| | [`/docs/llms.txt`](https://cut.pro/docs/llms.txt) | O índice de todas as páginas, nos três idiomas | | [`/docs/llms-full.txt`](https://cut.pro/docs/llms-full.txt) | A documentação inteira em um arquivo só | Cole um dos dois numa conversa e peça o que quiser. Qualquer página também responde em Markdown trocando a URL por `.md`, como em [`/docs/api-reference/quickstart.md`](https://cut.pro/docs/api-reference/quickstart.md). ## Próximo passo - [Prefere chamar a API direto?](https://cut.pro/docs/api-reference/quickstart): O Quickstart tem o fluxo inteiro em `curl`, Node.js e Python --- # Publicando nas redes > Como publicar clipes renderizados no TikTok, Instagram, YouTube e mais Depois de renderizar um clipe, você pode publicá-lo em uma ou mais contas conectadas com `POST /posts`. Um único post pode levar **vários clipes** para **várias contas** de uma vez. ## Antes de começar Você precisa de duas coisas: - **Um clipe renderizado**: O `editId` é o `edit_setting_id` que o [render](/docs/api-reference/quickstart) devolve quando concluído. É ele que identifica o vídeo a publicar - **Contas conectadas**: O `connectionId` vem de `GET /connections`. Conecte novas contas pelo OAuth dentro de [cut.pro](https://cut.pro): a API não conecta contas ## Como o corpo é montado Cada item de `videos` aponta um clipe (`editId`) para um ou mais destinos (`targets`). Cada destino combina uma conexão (`connectionId`) com a `metadata` específica daquela plataforma (título, privacidade, etc.). ```bash curl -X POST https://api.cut.pro/api/v1/posts \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "editId": "7412908490112775", "targets": [ { "connectionId": "7399001244553012", "metadata": { "tiktok": { "title": "Meu corte viral", "privacyLevel": "PUBLIC_TO_EVERYONE" } } }, { "connectionId": "7399001244553099", "metadata": { "youtube": { "title": "Meu corte viral", "description": "Cortes do episódio de hoje", "categoryId": "22", "privacyStatus": "public" } } } ] } ] }' ``` ```json title="Resposta 201" { "post_id": "7412909001223344", "item_count": 2, "scheduled_at": null, "status": "pending" } ``` A `metadata` muda por plataforma (TikTok, YouTube, Instagram, Threads, Bluesky, LinkedIn, Pinterest, Facebook). Os campos obrigatórios e as opções de cada uma estão detalhados na página do endpoint `POST /posts`, com o playground interativo. > Para **agendar** em vez de publicar agora, envie `scheduled_at` com uma data ISO 8601 no futuro. ## Acompanhe a publicação O post entra na fila e publica de forma assíncrona. Faça polling em `GET /posts/{id}`: ```bash curl https://api.cut.pro/api/v1/posts/7412909001223344 \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` O `status` do post evolui assim: | Status | Significado | |---|---| | `pending` | Na fila, ainda não começou | | `processing` | Publicando nas contas | | `completed` | Todos os itens publicaram | | `partial` | Alguns publicaram, outros falharam | | `failed` | Nenhum item publicou | O array `items` mostra o resultado por conta, que é o que importa quando o status é `partial`. ## Quando algo falha Itens falham de forma independente: um destino com erro **não derruba** os que já publicaram. - **Reenviar um item que falhou:** `POST /posts/{id}/items/{itemId}/retry` - **Remover um item** sem mexer nos demais: `DELETE /posts/{id}/items/{itemId}` - **Apagar o post inteiro:** `DELETE /posts/{id}` - **Editar antes de publicar** (por exemplo, ajustar o agendamento): `PATCH /posts/{id}` --- # Quickstart: seu primeiro clipe > Do link do vídeo ao MP4 renderizado, com requisições reais Sete requisições separam um link do YouTube de um clipe vertical renderizado. Este guia mostra as sete, na ordem, com a resposta de cada uma. Antes de começar, [gere uma chave de API](/docs/api-reference/autenticacao) e exporte-a no seu terminal: ```bash export CUTPRO_API_KEY="sua_chave" ``` > Todos os exemplos usam a URL base `https://api.cut.pro/api/v1` e enviam a chave no header `X-Api-Key`. Se a sua chave for multi-workspace, adicione também `X-Workspace-Id`. ## 1. Analise o vídeo `POST /clips/info` lê os metadados do link e calcula o custo. **Analisar não consome créditos.** ```bash 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" }' ``` ```json title="Resposta" { "video_id": "7412908365112823", "title": "Entrevista completa: 2 horas sobre carreira", "author": "Canal Exemplo", "platform": "youtube", "duration": 7245, "credits_cost": 121, "current_balance": 480, "credits_unlimited": false, "force_watermark": false } ``` Guarde o `video_id`: é ele que identifica o vídeo em todas as chamadas seguintes. > Quer clipar um arquivo do seu computador em vez de uma URL? Veja [Enviando um vídeo](/docs/api-reference/enviando-video) para o fluxo de upload. ## 2. Envie para clipagem `POST /clips` cria a submissão. **Os créditos são debitados aqui**, no valor que o passo anterior mostrou. ```bash 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 } }' ``` ```json title="Resposta 201" { "submission_id": "7412908371004912", "video_id": "7412908365112823", "status": "queued", "credits_charged": 30 } ``` O `timeframe` é opcional: omita para processar o vídeo inteiro, ou restrinja a um trecho (em segundos) para gastar menos. Acima, a primeira meia hora custou 30 créditos em vez de 121. > `strategy_id`, `template_id` e `source_language` (`auto`, `en`, `pt`) também são opcionais. Liste seus templates com `GET /templates` para obter os IDs. ## 3. Acompanhe até concluir Faça polling na submissão a cada 10 ou 15 segundos. O `status` passa por `queued`, `downloading`, `transcribing`, `video_analysis`, `analyzing` e `finalizing` até chegar em `completed` (ou `failed`). ```bash curl https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912 \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Resposta" { "submission_id": "7412908371004912", "video_id": "7412908365112823", "status": "analyzing", "error_code": null, "clips_count": 0, "queue_position": 2, "estimated_time": 340 } ``` Enquanto o vídeo está na fila, `queue_position` e `estimated_time` (em segundos) dizem quanto falta. Quando o `status` vira `completed`, `clips_count` traz quantos clipes a IA gerou. ## 4. Busque os clipes gerados ```bash curl https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/clips \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Resposta" { "clips": [ { "id": "7412908412887301", "title": "O erro que custou a primeira empresa", "rating": 9.2, "start_time": 412.5, "end_time": 461.8, "language": "pt", "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 } } ``` O `rating` é a nota da IA de 0 a 10: quanto maior, maior o potencial do corte. Ordene por ele para pegar os melhores primeiro. ## 5. Renderize um clipe `POST .../render` gera o MP4 final. Para aplicar um dos seus visuais antes, chame `POST .../apply_template` na submissão. ```bash curl -X POST \ https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/clips/7412908412887301/render \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Resposta 202" { "render_id": "7412908490112774", "edit_setting_id": "7412908490112775", "status": "queued", "output_resolution": "1080p", "has_watermark": false, "from_cache": false, "download_url": null } ``` Quando `from_cache` for `true`, a resposta vem com `200` e o `download_url` já preenchido: esse clipe já tinha sido renderizado com as mesmas configurações e você pode pular os dois passos seguintes. ## 6. Acompanhe o render ```bash curl https://api.cut.pro/api/v1/renders/7412908490112774 \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Resposta" { "render_id": "7412908490112774", "edit_setting_id": "7412908490112775", "status": "active", "progress": 64, "output_resolution": "1080p" } ``` O `status` passa por `queued`, `active` e `completed`, e `progress` vai de 0 a 100. ## 7. Baixe o MP4 ```bash curl https://api.cut.pro/api/v1/renders/7412908490112774/download \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Resposta" { "url": "https://media.cut.pro/render/7412908490112774.mp4?signature=...", "filename": "o-erro-que-custou-a-primeira-empresa.mp4" } ``` A `url` é assinada e **vale por uma hora**. Baixe o arquivo dentro desse prazo ou peça outra. ## O fluxo inteiro em um arquivo Os sete passos encadeados, com o polling já resolvido. Troque a URL do vídeo e rode. **Node.js** ```js showLineNumbers 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); ``` **Python** ```python showLineNumbers 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 = submission while 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 None while 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) ``` ## E agora? - [Publique nas redes](https://cut.pro/docs/api-reference/postagem): Leve os clipes renderizados para o TikTok, Instagram, YouTube e mais - [Workspace e créditos](https://cut.pro/docs/api-reference/saldo): Entenda o consumo de créditos e como consultar o saldo --- # Limites de requisição > Como funciona o rate limit da API e como lidar com o status 429 A API aplica um limite de requisições para proteger a plataforma. ## O limite - **1500 requisições por minuto**, por endereço IP - A janela é de **60 segundos** Esse limite é generoso para o uso normal, incluindo o polling de submissões e renders. Se você roda muitos processos a partir do mesmo IP, distribua as chamadas ao longo do tempo. ## Quando o limite é atingido Ao ultrapassar o limite, a API responde com: - Status **`429 Too Many Requests`** - Header **`Retry-After`** com o número de **segundos** que você deve esperar - Corpo: `{ "code": "RATE_LIMIT_EXCEEDED" }` ```http HTTP/1.1 429 Too Many Requests Retry-After: 60 { "code": "RATE_LIMIT_EXCEEDED" } ``` ## Como lidar - **Respeite o `Retry-After`**: espere o número de segundos indicado antes de tentar de novo - **Use backoff** em caso de chamadas repetidas, em vez de tentar imediatamente - **Espace o polling**: ao acompanhar uma submissão ou um render, consulte em intervalos de alguns segundos em vez de em loop apertado > A API não retorna headers `X-RateLimit-*`. Use o status `429` e o `Retry-After` para detectar e tratar o limite. --- # Workspace e créditos > Como o workspace, o plano e o saldo de créditos funcionam na API Toda chamada à API acontece dentro de um **workspace**, e é o saldo de créditos dele que é consumido quando você cria submissões. ## O workspace da requisição A chave de API resolve para um workspace em cada requisição (veja [Autenticação](/docs/api-reference/autenticacao) sobre chaves single e multi-workspace). Para inspecionar qual workspace foi resolvido, com plano, papel e número de membros: ```bash curl https://api.cut.pro/api/v1/workspace \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Resposta" { "id": "7401220118845503", "name": "Estúdio Exemplo", "is_personal": false, "role": "admin", "plan_name": "Pro", "plan_is_free": false, "credits": 480, "credits_unlimited": false, "member_count": 4 } ``` ## Como os créditos são consumidos **Os créditos pertencem ao workspace**, não à chave. Uma chave multi-workspace consome do workspace escolhido pelo header `X-Workspace-Id`. **A cobrança acontece na submissão.** Os créditos são debitados no momento em que você chama `POST /clips`. Analisar com `POST /clips/info` não custa nada e mostra o `credits_cost` antes de você se comprometer. **1 crédito equivale a 1 minuto processado.** O custo é proporcional ao trecho do vídeo que você manda processar (o `timeframe`), então restringir o trecho reduz o custo. Quando o saldo não cobre a submissão, `POST /clips` responde `402 INSUFFICIENT_CREDITS`, e o `extra` traz `credits_needed` e `current_balance`. ## Consultando o saldo Saldo atual do workspace: ```bash curl https://api.cut.pro/api/v1/balance \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Resposta" { "balance": 480, "unlimited": false, "unlimited_until": null } ``` Extrato de movimentações (créditos adicionados e consumidos): ```bash curl https://api.cut.pro/api/v1/balance/history \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` > Quer prever o custo antes de submeter? Chame `POST /clips/info` com a URL do vídeo: a resposta traz `credits_cost`, `discount_percent` e `current_balance`. --- # Authentication > How to authenticate your requests and select the workspace Every API request needs an **API key**. ## Generating your key Go to [API keys](https://cut.pro/studio/me/api-keys) in your account settings and click **New key**. Store the key somewhere safe: it is not shown again after it is created. > The key grants access to the credits and the connected accounts of the workspace. Keep it on your server, in an environment variable, never in browser or app code. ## Sending the key Send the key in **every** request, in one of two ways: ```bash title="Header X-Api-Key" curl https://api.cut.pro/api/v1/workspace \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```bash title="Authorization Bearer" curl https://api.cut.pro/api/v1/workspace \ -H "Authorization: Bearer $CUTPRO_API_KEY" ``` A missing, revoked or invalid key comes back like this: ```json title="Response 401" { "code": "UNAUTHORIZED", "message": "Unauthorized" } ``` ## Workspaces An API key is bound to **one or more workspaces** (scoped tokens, Cloudflare style). Credits, submissions, clips, and renders always belong to the workspace the request resolves to. **Single-workspace key** Every request operates on that workspace automatically. **No extra header is needed.** **Multi-workspace key** Send the `X-Workspace-Id` header on each request to choose which workspace the call hits: ```bash curl https://api.cut.pro/api/v1/balance \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "X-Workspace-Id: 7401220118845503" ``` Without the header, the response is `400 MISSING_WORKSPACE_ID`, including the list of allowed workspaces. > Call `GET /workspace` to inspect which workspace the current request resolved to, along with the plan and your role in it. ## Social connections Connected social accounts (TikTok, Instagram, YouTube, etc.) are the IDs you pass as `connectionId` when creating a post. The lifecycle of connections **is not exposed by the API**. To connect a new account, go to [cut.pro](https://cut.pro) and use the OAuth flow inside the app. --- # Submitting a video for clipping > The two ways to get a video_id: from a public URL or by uploading your own file Before generating clips you need a `video_id`. There are two ways to get one, and both converge on the same next step: calling `POST /clips` with that `video_id` (see the [Quickstart](/docs/en/api-reference/quickstart)). > The video must be **at least 1 minute** long. The maximum duration depends on your plan. ## Option A: by public URL The simplest way. Send the URL of a public video (YouTube, TikTok, Twitch, Kick, Vimeo and others) for analysis. **Analyzing costs no credits.** ```bash 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" }' ``` The response carries `video_id`, `credits_cost` (what you will be charged on submit), `current_balance` and the title and duration of the video. Keep the `video_id` and move on to `POST /clips`. ### When the link will not do Not every public link can be clipped. These cases come back as `422` with a `code` naming the reason: | `code` | What happened | |---|---| | `LIVE_STREAM` | The stream is still on air. Wait for it to end and become a video | | `PLAYLIST_URL` | The URL points to a playlist. Send the link of a single video | | `CHANNEL_URL` | The URL points to a channel. Send the link of a single video | | `SHORTS_NOT_SUPPORTED` | The video is already a vertical short, there is nothing to cut | | `AUDIO_ONLY` | The link has no video track | | `INVALID_DURATION` | Under 1 minute, or longer than your plan allows | | `INVALID_URL` | The platform is not supported or the address is wrong | A private, age-restricted or region-blocked video comes back as `403` (`PRIVATE_VIDEO`, `AGE_RESTRICTED`, `REGION_BLOCKED`). ## Option B: uploading your own file For videos on your machine, the upload takes **three steps** with a presigned URL, sending the bytes straight to storage. > Upload limits: **2 GB max** and the extensions **`.mp4`, `.mov`, `.webm`, `.mkv`**. The presigned URL expires in **1 hour**. 1. ### Start the upload Declare the file name and the content type. The response carries the `video_id`, the `upload_url` (where to send the bytes) and `expires_in` (seconds until it expires). ```bash curl -X POST https://api.cut.pro/api/v1/videos/upload \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "file_name": "my-stream.mp4", "content_type": "video/mp4" }' ``` 2. ### Send the bytes to the upload_url `PUT` the file straight to the `upload_url`. **Do not send your API key here** (the URL is already signed), and use the **same `Content-Type`** you declared in the previous step. ```bash curl -X PUT "UPLOAD_URL" \ -H "Content-Type: video/mp4" \ --data-binary @my-stream.mp4 ``` The `PUT` must return `200` before you move on. 3. ### Complete the upload Register the upload with the dimensions and the duration of the file (measured on your side). The response carries the video metadata and the `credits_cost`, same as the URL analysis. ```bash curl -X POST https://api.cut.pro/api/v1/videos/upload/complete \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_id": "VIDEO_ID", "file_name": "my-stream.mp4", "duration": 5400, "width": 1920, "height": 1080 }' ``` Only call this step after the `PUT` returned `200`. If the file is not in storage, the response is `404 FILE_NOT_FOUND`. If the duration is out of bounds, it returns `400 VIDEO_TOO_SHORT` or `VIDEO_TOO_LONG` (with `max_duration`). ## Next step - [Carry on in the Quickstart](https://cut.pro/docs/en/api-reference/quickstart): With the `video_id` in hand, pick up from the "Submit it for clipping" step (`POST /clips`) --- # API overview > Automate AI video clipping and rendering programmatically The **CutPro API** automates the whole AI clipping flow: you send a video link, the AI finds the best moments, and you get the clips ready to render and publish, all over HTTP requests. ## Base URL Every route uses this prefix: ``` https://api.cut.pro/api/v1 ``` Authentication is a key in the `X-Api-Key` header, and API access requires the Pro plan. See [Authentication](/docs/en/api-reference/autenticacao). ## The typical flow The API follows the same path as the studio, in steps you drive one request at a time: 1. **Analyze (free).** Preview the metadata and the credit cost of a public link, without being charged. `POST /clips/info` 2. **Submit for clipping.** Send the video in. Credits are charged right away. `POST /clips` 3. **Poll the submission.** Keep polling until `status` turns `completed` or `failed`. `GET /clips/{videoId}/submissions/{submissionId}` 4. **Fetch the generated clips.** Take what the AI produced, with timestamps, score and URLs. `GET /clips/{videoId}/submissions/{submissionId}/clips` 5. **Apply a template (optional).** Apply one of your templates to the clips in bulk. `POST /clips/{videoId}/submissions/{submissionId}/apply_template` 6. **Render each clip.** Produce the final MP4 and poll until it is done. `POST .../clips/{clipId}/render` and `GET /renders/{renderId}` 7. **Download and publish.** Download the rendered video and, if you want, publish it. `GET /renders/{renderId}/download` and `POST /posts` The [Quickstart](/docs/en/api-reference/quickstart) walks those seven steps with real requests and the response of each one. ## Errors Every error response has the same shape: a stable, upper-case `code` your code handles in a `switch`. The text a person reads is written by you, in their language. ```json title="Response 402" { "code": "INSUFFICIENT_CREDITS", "extra": { "credits_needed": 121, "current_balance": 40, "is_reclip": false, "scope": "workspace", "is_owner": true } } ``` | Status | When it happens | Example codes | |---|---|---| | `400` | Invalid body or an operation out of order | `VALIDATION_ERROR`, `TIMEFRAME_OUT_OF_BOUNDS`, `INVALID_METADATA` | | `401` | Key missing, revoked or invalid | `UNAUTHORIZED` | | `402` | Not enough credits for the submission | `INSUFFICIENT_CREDITS` | | `403` | The video or the resource is not reachable | `PRIVATE_VIDEO`, `REGION_BLOCKED`, `DAILY_LIMIT_EXCEEDED` | | `404` | The resource does not exist in this workspace | `VIDEO_NOT_FOUND`, `RENDER_NOT_FOUND` | | `409` | The resource is in a state that rejects the operation | `VIDEO_ALREADY_PROCESSING`, `DUPLICATE_POST` | | `410` | The result existed and has expired | `SUBMISSION_EXPIRED`, `RENDER_FILE_EXPIRED` | | `422` | The link is valid but the video cannot be clipped | `LIVE_STREAM`, `PLAYLIST_URL`, `AUDIO_ONLY` | | `429` | Rate limit reached | `RATE_LIMIT_EXCEEDED` | When the `code` is `VALIDATION_ERROR`, the response also carries an `errors` array naming the field that failed. The codes each route can return are listed in the [reference](/docs/reference), response by response. ## Quality and rights > The AI picks moments by what is said, not by what is shown: a video with clean audio makes better clips. Only use content you have permission to edit. CutPro does not remove third-party watermarks. ## Next steps - [Authentication](https://cut.pro/docs/en/api-reference/autenticacao): How to create your key and send it with requests, workspaces included - [Quickstart](https://cut.pro/docs/en/api-reference/quickstart): A full example, from the link to the rendered MP4 - [Workspace and credits](https://cut.pro/docs/en/api-reference/saldo): How credits are spent on each submission - [Publishing](https://cut.pro/docs/en/api-reference/postagem): How to publish rendered clips to social networks --- # Connect via MCP > Use CutPro inside Claude, ChatGPT and other AI tools through the Model Context Protocol **MCP (Model Context Protocol)** is an open standard that connects AI tools to external services. Connected to CutPro, the model runs the whole flow on its own: it analyzes the video, clips it, renders it and hands back the link. > API access requires the **Pro** plan. Create a key in [API keys](https://cut.pro/studio/me/api-keys) and use it as `CUTPRO_API_KEY`. ## Local: Claude Code, Cursor, VS Code The server runs on your machine through `npx` and exposes the v1 API endpoints as tools, covering workspace, balance, videos and uploads, clipping, clips, templates, renders, publishing and connections. **Claude Code** ```bash claude mcp add cutpro \ --env CUTPRO_API_KEY=$CUTPRO_API_KEY \ -- npx -y @cutpro/mcp ``` **Claude Desktop, Cursor, VS Code** ```json title="MCP server configuration" { "mcpServers": { "cutpro": { "command": "npx", "args": ["-y", "@cutpro/mcp"], "env": { "CUTPRO_API_KEY": "your_key" } } } } ``` > Multi-workspace key? Add `CUTPRO_WORKSPACE_ID` to the `env` as well. ### An example Once connected, just ask in plain language: > "Analyze this YouTube video, generate the clips, and send me the 3 best-rated ones already rendered." The AI chains the tools by itself: `analyze_video`, `submit_clipping`, `get_submission` (until it finishes), `list_clips`, `render_clip`, `get_render` and `get_render_download`. ## Remote: ChatGPT and Claude.ai The same server runs at `https://mcp.cut.pro` with full **OAuth 2.1** (metadata discovery, dynamic client registration, PKCE and refresh tokens). It is the path for clients that run in the cloud and cannot execute `npx`. 1. ### Add the connector In ChatGPT (Connectors) or Claude.ai (Settings, Connectors, Add custom connector), enter the URL `https://mcp.cut.pro`. 2. ### Authorize with your key The client opens a consent page. Paste your [API key](https://cut.pro/studio/me/api-keys): it is validated and access is granted. 3. ### Done The client can now use CutPro tools in conversations. ## Let the AI read these docs For questions about the product, without running anything, the documentation is published as plain text in the format models read directly: | Address | What it holds | |---|---| | [`/docs/llms.txt`](https://cut.pro/docs/llms.txt) | The index of every page, in all three languages | | [`/docs/llms-full.txt`](https://cut.pro/docs/llms-full.txt) | The whole documentation in a single file | Paste either one into a conversation and ask away. Any page also answers in Markdown if you swap the URL for `.md`, as in [`/docs/en/api-reference/quickstart.md`](https://cut.pro/docs/en/api-reference/quickstart.md). ## Next step - [Rather call the API directly?](https://cut.pro/docs/en/api-reference/quickstart): The Quickstart has the whole flow in `curl`, Node.js and Python --- # Publishing to social networks > How to publish rendered clips to TikTok, Instagram, YouTube, and more After rendering a clip, you can publish it to one or more connected accounts with `POST /posts`. A single post can carry **multiple clips** to **multiple accounts** at once. ## Before you start You need two things: - **A rendered clip**: The `editId` is the `edit_setting_id` that the [render](/docs/en/api-reference/quickstart) returns when complete. It is what identifies the video to publish. - **Connected accounts**: The `connectionId` comes from `GET /connections`. Connect new accounts via OAuth inside [cut.pro](https://cut.pro). The API does not connect accounts. ## How the body is built Each item in `videos` points a clip (`editId`) to one or more targets (`targets`). Each target combines a connection (`connectionId`) with the `metadata` specific to that platform (title, privacy, etc.). ```bash curl -X POST https://api.cut.pro/api/v1/posts \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "editId": "EDIT_SETTING_ID", "targets": [ { "connectionId": "CONEXAO_TIKTOK", "metadata": { "tiktok": { "title": "Meu corte viral", "privacyLevel": "PUBLIC_TO_EVERYONE" } } }, { "connectionId": "CONEXAO_YOUTUBE", "metadata": { "youtube": { "title": "Meu corte viral", "description": "Cortes do episódio de hoje", "categoryId": "22", "privacyStatus": "public" } } } ] } ] }' ``` ```json title="Response 201" { "post_id": "7412909001223344", "item_count": 2, "scheduled_at": null, "status": "pending" } ``` The `metadata` changes per platform (TikTok, YouTube, Instagram, Threads, Bluesky, LinkedIn, Pinterest, Facebook). The required fields and options for each are detailed on the endpoint page **`POST /posts`**, with the interactive playground. > To **schedule** instead of publishing now, send `scheduled_at` with an ISO 8601 date in the future. ## Track the publishing The post is queued and publishes asynchronously. Poll `GET /posts/{id}`: ```bash curl https://api.cut.pro/api/v1/posts/POST_ID \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` The post `status` evolves like this: | Status | Meaning | |---|---| | `pending` | In the queue, not started yet | | `processing` | Publishing to the accounts | | `completed` | All items published | | `partial` | Some published, others failed | | `failed` | No item published | The `items` array shows the result per account, useful when the status is `partial`. ## When something fails Items fail independently: a target with an error **does not bring down** the ones that already published. - **Retry a failed item:** `POST /posts/{id}/items/{itemId}/retry` - **Remove an item** without touching the others: `DELETE /posts/{id}/items/{itemId}` - **Delete the whole post:** `DELETE /posts/{id}` - **Edit before publishing** (e.g. adjust scheduling): `PATCH /posts/{id}` --- # 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](/docs/en/api-reference/autenticacao) and export it in your terminal: ```bash 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.** ```bash 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" }' ``` ```json title="Response" { "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. > Want to clip a file from your own machine instead of a URL? See [Submitting a video](/docs/en/api-reference/enviando-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. ```bash 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 } }' ``` ```json title="Response 201" { "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. > `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`). ```bash curl https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912 \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Response" { "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 ```bash curl https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/clips \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="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. ```bash curl -X POST \ https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/clips/7412908412887301/render \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Response 202" { "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 ```bash curl https://api.cut.pro/api/v1/renders/7412908490112774 \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Response" { "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 ```bash curl https://api.cut.pro/api/v1/renders/7412908490112774/download \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="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. **Node.js** ```js showLineNumbers 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); ``` **Python** ```python showLineNumbers 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 = submission while 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 None while 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? - [Publish to networks](https://cut.pro/docs/en/api-reference/postagem): Take the rendered clips to TikTok, Instagram, YouTube and more - [Workspace and credits](https://cut.pro/docs/en/api-reference/saldo): How credits are spent and how to check your balance --- # Rate limits > How the API rate limit works and how to handle the 429 status The API applies a request limit to protect the platform. ## The limit - **1500 requests per minute**, per IP address - The window is **60 seconds** This limit is generous for normal usage, including polling submissions and renders. If you run many processes from the same IP, spread your calls out over time. ## When the limit is hit When you exceed the limit, the API responds with: - Status **`429 Too Many Requests`** - A **`Retry-After`** header with the number of **seconds** you should wait - Body: `{ "code": "RATE_LIMIT_EXCEEDED" }` ```http HTTP/1.1 429 Too Many Requests Retry-After: 60 { "code": "RATE_LIMIT_EXCEEDED" } ``` ## How to handle it - **Respect the `Retry-After`**: wait the indicated number of seconds before trying again - **Use backoff** for repeated calls, instead of retrying immediately - **Space out polling**: when tracking a submission or a render, check at intervals of a few seconds instead of in a tight loop > The API does not return `X-RateLimit-*` headers. Use the `429` status and `Retry-After` to detect and handle the limit. --- # Workspace and credits > How the workspace, the plan and the credit balance work in the API Every API call happens inside a **workspace**, and it is that workspace's credit balance that gets spent when you create submissions. ## The workspace of a request The API key resolves to a workspace on every request (see [Authentication](/docs/en/api-reference/autenticacao) about single and multi-workspace keys). To inspect which workspace was resolved, with plan, role and member count: ```bash curl https://api.cut.pro/api/v1/workspace \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Response" { "id": "7401220118845503", "name": "Example Studio", "is_personal": false, "role": "admin", "plan_name": "Pro", "plan_is_free": false, "credits": 480, "credits_unlimited": false, "member_count": 4 } ``` ## How credits are spent **Credits belong to the workspace**, not to the key. A multi-workspace key spends from the workspace picked by the `X-Workspace-Id` header. **The charge happens on submission.** Credits are deducted the moment you call `POST /clips`. Analyzing with `POST /clips/info` costs nothing and shows the `credits_cost` before you commit. **1 credit equals 1 processed minute.** The cost is proportional to the stretch of video you send for processing (the `timeframe`), so narrowing the stretch lowers the cost. When the balance does not cover the submission, `POST /clips` answers `402 INSUFFICIENT_CREDITS`, and `extra` carries `credits_needed` and `current_balance`. ## Checking the balance Current workspace balance: ```bash curl https://api.cut.pro/api/v1/balance \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Response" { "balance": 480, "unlimited": false, "unlimited_until": null } ``` Ledger of movements (credits added and spent): ```bash curl https://api.cut.pro/api/v1/balance/history \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` > Want to predict the cost before submitting? Call `POST /clips/info` with the video URL: the response carries `credits_cost`, `discount_percent` and `current_balance`. --- # CutPro Documentation > Official Cut.Pro documentation: getting started with the API, authentication, credits, publishing to networks and the full endpoint reference. Turn long videos into vertical clips for TikTok, Instagram and YouTube Shorts using artificial intelligence, programmatically. You send a link, the AI picks the best moments, and you get the clips ready to render and publish. [Start with the Quickstart](https://cut.pro/docs/en/api-reference/quickstart) [Browse every endpoint](https://cut.pro/docs/reference) ## Your first request Analyzing a video shows the cost in credits before you spend anything. It is the call every flow starts from. **curl** ```bash 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" }' ``` **Node.js** ```js const response = await fetch("https://api.cut.pro/api/v1/clips/info", { method: "POST", headers: { "X-Api-Key": process.env.CUTPRO_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }), }); const video = await response.json(); ``` **Python** ```python response = requests.post( "https://api.cut.pro/api/v1/clips/info", headers={"X-Api-Key": os.environ["CUTPRO_API_KEY"]}, json={"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}, ) video = response.json() ``` ```json title="Response" { "video_id": "7412908365112823", "title": "Full interview: 2 hours on building a career", "author": "Example Channel", "platform": "youtube", "duration": 7245, "credits_cost": 121, "credits_original": 121, "discount_percent": 0, "current_balance": 480, "credits_unlimited": false, "force_watermark": false } ``` With the `video_id` in hand, `POST /clips` submits the video for clipping. The [Quickstart](/docs/en/api-reference/quickstart) picks up from there and goes to the rendered MP4. ## Start here - [Quickstart](https://cut.pro/docs/en/api-reference/quickstart): From the link to the rendered MP4, requests in order - [Authentication](https://cut.pro/docs/en/api-reference/autenticacao): How to create your key and pick the workspace - [API overview](https://cut.pro/docs/en/api-reference/introducao): The whole flow, from the video link to the published clip - [API reference](https://cut.pro/docs/reference): All 44 endpoints, with examples and a client to try them ## Guides - [Submitting a video](https://cut.pro/docs/en/api-reference/enviando-video): From a public URL or by uploading your own file - [Workspace and credits](https://cut.pro/docs/en/api-reference/saldo): How usage works and how to check your balance - [Publishing to networks](https://cut.pro/docs/en/api-reference/postagem): One clip to many accounts, with scheduling - [Rate limits](https://cut.pro/docs/en/api-reference/rate-limit): How many calls per minute and how to handle a 429 - [Connect via MCP](https://cut.pro/docs/en/api-reference/mcp): Use CutPro inside Claude, ChatGPT and your editor - [API keys](https://cut.pro/studio/me/api-keys): Create and revoke keys in your account settings --- # Autenticación > Cómo autenticar tus peticiones y seleccionar el workspace Toda petición a la API necesita una **clave de API**. ## Generando tu clave Entra en [Claves de API](https://cut.pro/studio/me/api-keys) en los ajustes de tu cuenta y pulsa **Nueva clave**. Guarda la clave en un lugar seguro: no se vuelve a mostrar después de creada. > La clave da acceso a los créditos y a las cuentas conectadas del workspace. Mantenla en tu servidor, en una variable de entorno, nunca en el código del navegador ni de la app. ## Enviando la clave Envía la clave en **todas** las peticiones, de una de las dos formas: ```bash title="Header X-Api-Key" curl https://api.cut.pro/api/v1/workspace \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```bash title="Authorization Bearer" curl https://api.cut.pro/api/v1/workspace \ -H "Authorization: Bearer $CUTPRO_API_KEY" ``` Una clave ausente, revocada o inválida vuelve así: ```json title="Respuesta 401" { "code": "UNAUTHORIZED", "message": "Unauthorized" } ``` ## Workspaces Una clave de API está vinculada a **uno o más workspaces** (tokens con alcance, al estilo de Cloudflare). Los créditos, los envíos, los clips y los renders siempre pertenecen al workspace que resuelva la petición. **Clave de un workspace** Toda petición opera en ese workspace automáticamente. **No hace falta ningún header extra.** **Clave multi-workspace** Envía el header `X-Workspace-Id` en cada petición para elegir a qué workspace llega la llamada: ```bash curl https://api.cut.pro/api/v1/balance \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "X-Workspace-Id: 7401220118845503" ``` Sin el header, la respuesta es `400 MISSING_WORKSPACE_ID`, con la lista de workspaces permitidos. > Llama a `GET /workspace` para ver qué workspace resolvió la petición actual, junto con el plan y tu rol en él. ## Conexiones sociales Las cuentas sociales conectadas (TikTok, Instagram, YouTube, etc.) son los IDs que pasas como `connectionId` al crear una publicación. El ciclo de vida de las conexiones **no se expone por la API**: para conectar una cuenta nueva, entra en [cut.pro](https://cut.pro) y usa el flujo de OAuth dentro de la app. --- # Enviando un vídeo para recortar > Las dos formas de obtener un video_id: por URL pública o subiendo tu propio archivo Antes de generar clips necesitas un `video_id`. Hay dos formas de conseguirlo, y las dos llevan al mismo paso siguiente: llamar a `POST /clips` con ese `video_id` (mira el [Quickstart](/docs/es/api-reference/quickstart)). > El vídeo debe durar **como mínimo 1 minuto**. La duración máxima depende de tu plan. ## Opción A: por URL pública La forma más simple. Envía la URL de un vídeo público (YouTube, TikTok, Twitch, Kick, Vimeo y otros) para analizarlo. **Analizar no cuesta créditos.** ```bash 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" }' ``` La respuesta trae `video_id`, `credits_cost` (lo que se cobrará al enviar), `current_balance` y el título y la duración del vídeo. Guarda el `video_id` y sigue con `POST /clips`. ### Cuando el enlace no sirve No todo enlace público se puede recortar. Estos casos vuelven con `422` y un `code` que dice el motivo: | `code` | Qué pasó | |---|---| | `LIVE_STREAM` | La emisión sigue en directo. Espera a que termine y quede como vídeo | | `PLAYLIST_URL` | La URL es de una lista de reproducción. Envía el enlace de un vídeo | | `CHANNEL_URL` | La URL es de un canal. Envía el enlace de un vídeo | | `SHORTS_NOT_SUPPORTED` | El vídeo ya es un corto vertical, no hay nada que recortar | | `AUDIO_ONLY` | El enlace no tiene pista de vídeo | | `INVALID_DURATION` | Menos de 1 minuto, o más de lo que permite tu plan | | `INVALID_URL` | La plataforma no está soportada o la dirección está mal | Un vídeo privado, con restricción de edad o bloqueado por región vuelve con `403` (`PRIVATE_VIDEO`, `AGE_RESTRICTED`, `REGION_BLOCKED`). ## Opción B: subiendo tu propio archivo Para vídeos de tu ordenador, la subida son **tres pasos** con una URL prefirmada (presigned), enviando los bytes directo al almacenamiento. > Límites de la subida: **máximo 2 GB** y las extensiones **`.mp4`, `.mov`, `.webm`, `.mkv`**. La URL prefirmada caduca en **1 hora**. 1. ### Inicia la subida Declara el nombre del archivo y el tipo de contenido. La respuesta trae el `video_id`, la `upload_url` (adónde enviar los bytes) y `expires_in` (segundos hasta que caduque). ```bash curl -X POST https://api.cut.pro/api/v1/videos/upload \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "file_name": "mi-directo.mp4", "content_type": "video/mp4" }' ``` 2. ### Envía los bytes a la upload_url Haz un `PUT` del archivo directo a la `upload_url`. **No envíes aquí tu clave de API** (la URL ya está firmada), y usa el **mismo `Content-Type`** que declaraste en el paso anterior. ```bash curl -X PUT "UPLOAD_URL" \ -H "Content-Type: video/mp4" \ --data-binary @mi-directo.mp4 ``` El `PUT` debe devolver `200` antes de seguir. 3. ### Finaliza la subida Registra la subida indicando las dimensiones y la duración del archivo (medidas por tu lado). La respuesta trae los metadatos del vídeo y el `credits_cost`, igual que el análisis por URL. ```bash curl -X POST https://api.cut.pro/api/v1/videos/upload/complete \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "video_id": "VIDEO_ID", "file_name": "mi-directo.mp4", "duration": 5400, "width": 1920, "height": 1080 }' ``` Llama a este paso solo después de que el `PUT` devuelva `200`. Si el archivo no está en el almacenamiento, la respuesta es `404 FILE_NOT_FOUND`. Si la duración se sale de los límites, devuelve `400 VIDEO_TOO_SHORT` o `VIDEO_TOO_LONG` (con `max_duration`). ## Siguiente paso - [Sigue en el Quickstart](https://cut.pro/docs/es/api-reference/quickstart): Con el `video_id` en la mano, continúa desde el paso "Envíalo al recorte" (`POST /clips`) --- # Visión general de la API > Automatiza el recorte y la renderización de vídeos con IA, de forma programática La **API de CutPro** automatiza todo el flujo de recorte con IA: envías el enlace de un vídeo, la IA encuentra los mejores momentos y recibes los clips listos para renderizar y publicar, todo por peticiones HTTP. ## URL base Todas las rutas usan este prefijo: ``` https://api.cut.pro/api/v1 ``` La autenticación es una clave en la cabecera `X-Api-Key`, y el acceso a la API exige el plan Pro. Mira [Autenticación](/docs/es/api-reference/autenticacao). ## El flujo típico La API sigue el mismo camino que el estudio, en etapas que controlas petición a petición: 1. **Analizar (sin coste).** Previsualiza los metadatos y el coste en créditos de un enlace público, sin que se te cobre. `POST /clips/info` 2. **Enviar al recorte.** Manda el vídeo. Los créditos se cobran en el momento. `POST /clips` 3. **Consultar el envío.** Haz polling hasta que el `status` pase a `completed` o `failed`. `GET /clips/{videoId}/submissions/{submissionId}` 4. **Recuperar los clips generados.** Toma lo que produjo la IA, con marcas de tiempo, nota y URLs. `GET /clips/{videoId}/submissions/{submissionId}/clips` 5. **Aplicar una plantilla (opcional).** Aplica una de tus plantillas a los clips en lote. `POST /clips/{videoId}/submissions/{submissionId}/apply_template` 6. **Renderizar cada clip.** Genera el MP4 final y haz polling hasta que termine. `POST .../clips/{clipId}/render` y `GET /renders/{renderId}` 7. **Descargar y publicar.** Descarga el vídeo renderizado y, si quieres, publícalo. `GET /renders/{renderId}/download` y `POST /posts` El [Quickstart](/docs/es/api-reference/quickstart) recorre esos siete pasos con peticiones reales y la respuesta de cada una. ## Errores Toda respuesta de error tiene la misma forma: un `code` estable, en mayúsculas, que tu código trata en un `switch`. El texto que lee la persona lo escribes tú, en su idioma. ```json title="Respuesta 402" { "code": "INSUFFICIENT_CREDITS", "extra": { "credits_needed": 121, "current_balance": 40, "is_reclip": false, "scope": "workspace", "is_owner": true } } ``` | Status | Cuándo ocurre | Ejemplos de `code` | |---|---|---| | `400` | Cuerpo inválido u operación fuera de orden | `VALIDATION_ERROR`, `TIMEFRAME_OUT_OF_BOUNDS`, `INVALID_METADATA` | | `401` | Clave ausente, revocada o inválida | `UNAUTHORIZED` | | `402` | Créditos insuficientes para el envío | `INSUFFICIENT_CREDITS` | | `403` | El vídeo o el recurso no está accesible | `PRIVATE_VIDEO`, `REGION_BLOCKED`, `DAILY_LIMIT_EXCEEDED` | | `404` | El recurso no existe en ese workspace | `VIDEO_NOT_FOUND`, `RENDER_NOT_FOUND` | | `409` | El recurso está en un estado que no acepta la operación | `VIDEO_ALREADY_PROCESSING`, `DUPLICATE_POST` | | `410` | El resultado existió y caducó | `SUBMISSION_EXPIRED`, `RENDER_FILE_EXPIRED` | | `422` | El enlace es válido pero el vídeo no sirve para recortar | `LIVE_STREAM`, `PLAYLIST_URL`, `AUDIO_ONLY` | | `429` | Límite de peticiones alcanzado | `RATE_LIMIT_EXCEEDED` | Cuando el `code` es `VALIDATION_ERROR`, la respuesta trae además un `errors` con el campo que falló. Los códigos que puede devolver cada ruta están listados en la [referencia](/docs/reference), respuesta por respuesta. ## Calidad y derechos > La IA elige los momentos por lo que se dice, no por lo que se ve: un vídeo con audio limpio da mejores clips. Usa solo contenido que tengas permiso para editar. CutPro no quita marcas de agua de terceros. ## Próximos pasos - [Autenticación](https://cut.pro/docs/es/api-reference/autenticacao): Cómo generar tu clave y enviarla en las peticiones, workspaces incluidos - [Quickstart](https://cut.pro/docs/es/api-reference/quickstart): Un ejemplo completo, del enlace al MP4 renderizado - [Workspace y créditos](https://cut.pro/docs/es/api-reference/saldo): Cómo se consumen los créditos en cada envío - [Publicación](https://cut.pro/docs/es/api-reference/postagem): Cómo publicar los clips renderizados en las redes sociales --- # Conectar vía MCP > Usa CutPro dentro de Claude, ChatGPT y otras herramientas de IA mediante el Model Context Protocol El **MCP (Model Context Protocol)** es un estándar abierto que conecta herramientas de IA con servicios externos. Conectado a CutPro, el modelo ejecuta el flujo entero solo: analiza el vídeo, lo recorta, lo renderiza y devuelve el enlace. > El acceso a la API exige el plan **Pro**. Genera una clave en [Claves de API](https://cut.pro/studio/me/api-keys) y úsala como `CUTPRO_API_KEY`. ## Local: Claude Code, Cursor, VS Code El servidor corre en tu máquina con `npx` y expone los endpoints de la API v1 como herramientas, cubriendo workspace, saldo, vídeos y subidas, recorte, clips, plantillas, renders, publicación y conexiones. **Claude Code** ```bash claude mcp add cutpro \ --env CUTPRO_API_KEY=$CUTPRO_API_KEY \ -- npx -y @cutpro/mcp ``` **Claude Desktop, Cursor, VS Code** ```json title="Configuración de servidores MCP" { "mcpServers": { "cutpro": { "command": "npx", "args": ["-y", "@cutpro/mcp"], "env": { "CUTPRO_API_KEY": "tu_clave" } } } } ``` > ¿Clave de varios workspaces? Añade también `CUTPRO_WORKSPACE_ID` al `env`. ### Un ejemplo Una vez conectado, basta con pedirlo en lenguaje natural: > "Analiza este vídeo de YouTube, genera los clips y mándame los 3 de mejor nota ya renderizados." La IA encadena las herramientas sola: `analyze_video`, `submit_clipping`, `get_submission` (hasta que termine), `list_clips`, `render_clip`, `get_render` y `get_render_download`. ## Remoto: ChatGPT y Claude.ai El mismo servidor corre en `https://mcp.cut.pro` con **OAuth 2.1** completo (descubrimiento de metadatos, registro dinámico de cliente, PKCE y refresh token). Es el camino para los clientes que corren en la nube y no ejecutan `npx`. 1. ### Añade el conector En ChatGPT (Connectors) o en Claude.ai (Settings, Connectors, Add custom connector), indica la URL `https://mcp.cut.pro`. 2. ### Autoriza con tu clave El cliente abre una página de consentimiento. Pega tu [clave de API](https://cut.pro/studio/me/api-keys): se valida y se concede el acceso. 3. ### Listo El cliente ya puede usar las herramientas de CutPro en las conversaciones. ## Deja que la IA lea esta documentación Para preguntas sobre el producto, sin ejecutar nada, la documentación se publica en texto plano en el formato que los modelos leen directamente: | Dirección | Qué trae | |---|---| | [`/docs/llms.txt`](https://cut.pro/docs/llms.txt) | El índice de todas las páginas, en los tres idiomas | | [`/docs/llms-full.txt`](https://cut.pro/docs/llms-full.txt) | La documentación entera en un solo archivo | Pega cualquiera de los dos en una conversación y pregunta. Cualquier página responde también en Markdown si cambias la URL por `.md`, como en [`/docs/es/api-reference/quickstart.md`](https://cut.pro/docs/es/api-reference/quickstart.md). ## Siguiente paso - [¿Prefieres llamar a la API directamente?](https://cut.pro/docs/es/api-reference/quickstart): El Quickstart tiene el flujo entero en `curl`, Node.js y Python --- # Publicando en las redes > Cómo publicar clips renderizados en TikTok, Instagram, YouTube y más Después de renderizar un clip puedes publicarlo en una o varias cuentas conectadas con `POST /posts`. Una sola publicación puede llevar **varios clips** a **varias cuentas** de una vez. ## Antes de empezar Necesitas dos cosas: - **Un clip renderizado**: El `editId` es el `edit_setting_id` que devuelve el [render](/docs/es/api-reference/quickstart) al completarse. Es lo que identifica el vídeo a publicar. - **Cuentas conectadas**: El `connectionId` viene de `GET /connections`. Conecta cuentas nuevas con el OAuth dentro de [cut.pro](https://cut.pro): la API no conecta cuentas. ## Cómo se arma el cuerpo Cada elemento de `videos` apunta un clip (`editId`) a uno o más destinos (`targets`). Cada destino combina una conexión (`connectionId`) con la `metadata` específica de esa plataforma (título, privacidad, etc.). ```bash curl -X POST https://api.cut.pro/api/v1/posts \ -H "X-Api-Key: $CUTPRO_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "videos": [ { "editId": "EDIT_SETTING_ID", "targets": [ { "connectionId": "CONEXION_TIKTOK", "metadata": { "tiktok": { "title": "Mi corte viral", "privacyLevel": "PUBLIC_TO_EVERYONE" } } }, { "connectionId": "CONEXION_YOUTUBE", "metadata": { "youtube": { "title": "Mi corte viral", "description": "Cortes del episodio de hoy", "categoryId": "22", "privacyStatus": "public" } } } ] } ] }' ``` ```json title="Respuesta 201" { "post_id": "7412909001223344", "item_count": 2, "scheduled_at": null, "status": "pending" } ``` La `metadata` cambia según la plataforma (TikTok, YouTube, Instagram, Threads, Bluesky, LinkedIn, Pinterest, Facebook). Los campos obligatorios y las opciones de cada una están detallados en la página del endpoint **`POST /posts`**, con el playground interactivo. > Para **programar** en vez de publicar ya, envía `scheduled_at` con una fecha ISO 8601 en el futuro. ## Sigue la publicación La publicación entra en la cola y publica de forma asíncrona. Haz polling en `GET /posts/{id}`: ```bash curl https://api.cut.pro/api/v1/posts/POST_ID \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` El `status` de la publicación evoluciona así: | Estado | Significado | |---|---| | `pending` | En cola, todavía no empezó | | `processing` | Publicando en las cuentas | | `completed` | Todos los elementos publicaron | | `partial` | Algunos publicaron y otros fallaron | | `failed` | Ningún elemento publicó | El array `items` muestra el resultado por cuenta, útil cuando el estado es `partial`. ## Cuando algo falla Los elementos fallan de forma independiente: un destino con error **no tumba** a los que ya publicaron. - **Reintentar un elemento que falló:** `POST /posts/{id}/items/{itemId}/retry` - **Quitar un elemento** sin tocar los demás: `DELETE /posts/{id}/items/{itemId}` - **Borrar la publicación entera:** `DELETE /posts/{id}` - **Editar antes de publicar** (por ejemplo, ajustar la programación): `PATCH /posts/{id}` --- # Quickstart: tu primer clip > Del enlace del vídeo al MP4 renderizado, con peticiones reales 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](/docs/es/api-reference/autenticacao) y expórtala en tu terminal: ```bash export CUTPRO_API_KEY="tu_clave" ``` > Todos los ejemplos usan la URL base `https://api.cut.pro/api/v1` y envían la clave en la cabecera `X-Api-Key`. Si tu clave cubre varios workspaces, añade también `X-Workspace-Id`. ## 1. Analiza el vídeo `POST /clips/info` lee los metadatos del enlace y calcula el coste. **Analizar no consume créditos.** ```bash 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" }' ``` ```json title="Respuesta" { "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. > ¿Quieres recortar un archivo de tu ordenador en vez de una URL? Mira [Enviando un vídeo](/docs/es/api-reference/enviando-video) para el flujo de subida. ## 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. ```bash 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 } }' ``` ```json title="Respuesta 201" { "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. > `strategy_id`, `template_id` y `source_language` (`auto`, `en`, `pt`) también son opcionales. Lista tus plantillas con `GET /templates` para obtener los IDs. ## 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`). ```bash curl https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912 \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Respuesta" { "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 ```bash curl https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/clips \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Respuesta" { "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 `POST .../render` genera el MP4 final. Para aplicar antes uno de tus estilos, llama a `POST .../apply_template` en el envío. ```bash curl -X POST \ https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/clips/7412908412887301/render \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Respuesta 202" { "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 ```bash curl https://api.cut.pro/api/v1/renders/7412908490112774 \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Respuesta" { "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 ```bash curl https://api.cut.pro/api/v1/renders/7412908490112774/download \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Respuesta" { "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 Los siete pasos encadenados, con el polling ya resuelto. Cambia la URL del vídeo y ejecútalo. **Node.js** ```js showLineNumbers 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); ``` **Python** ```python showLineNumbers 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 = submission while 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 None while 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? - [Publica en redes](https://cut.pro/docs/es/api-reference/postagem): Lleva los clips renderizados a TikTok, Instagram, YouTube y más - [Workspace y créditos](https://cut.pro/docs/es/api-reference/saldo): Cómo se consumen los créditos y cómo consultar el saldo --- # Límites de solicitudes > Cómo funciona el rate limit de la API y cómo tratar el estado 429 La API aplica un límite de peticiones para proteger la plataforma. ## El límite - **1500 peticiones por minuto**, por dirección IP - La ventana es de **60 segundos** Ese límite es generoso para el uso normal, incluido el polling de envíos y renders. Si ejecutas muchos procesos desde la misma IP, reparte las llamadas a lo largo del tiempo. ## Cuando se alcanza el límite Al superar el límite, la API responde con: - Estado **`429 Too Many Requests`** - Header **`Retry-After`** con el número de **segundos** que debes esperar - Cuerpo: `{ "code": "RATE_LIMIT_EXCEEDED" }` ```http HTTP/1.1 429 Too Many Requests Retry-After: 60 { "code": "RATE_LIMIT_EXCEEDED" } ``` ## Cómo tratarlo - **Respeta el `Retry-After`**: espera los segundos indicados antes de reintentar - **Usa backoff** en llamadas repetidas, en lugar de reintentar de inmediato - **Espacia el polling**: al seguir un envío o un render, consulta cada pocos segundos en vez de en bucle cerrado > La API no devuelve headers `X-RateLimit-*`. Usa el estado `429` y el `Retry-After` para detectar y tratar el límite. --- # Workspace y créditos > Cómo funcionan el workspace, el plan y el saldo de créditos en la API Toda llamada a la API ocurre dentro de un **workspace**, y es su saldo de créditos el que se consume cuando creas envíos. ## El workspace de la petición La clave de API resuelve a un workspace en cada petición (mira [Autenticación](/docs/es/api-reference/autenticacao) sobre claves de uno y de varios workspaces). Para inspeccionar qué workspace se resolvió, con plan, rol y número de miembros: ```bash curl https://api.cut.pro/api/v1/workspace \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Respuesta" { "id": "7401220118845503", "name": "Estudio de ejemplo", "is_personal": false, "role": "admin", "plan_name": "Pro", "plan_is_free": false, "credits": 480, "credits_unlimited": false, "member_count": 4 } ``` ## Cómo se consumen los créditos **Los créditos son del workspace**, no de la clave. Una clave de varios workspaces consume del workspace elegido por la cabecera `X-Workspace-Id`. **El cobro ocurre en el envío.** Los créditos se descuentan en el momento en que llamas a `POST /clips`. Analizar con `POST /clips/info` no cuesta nada y muestra el `credits_cost` antes de que te comprometas. **1 crédito equivale a 1 minuto procesado.** El coste es proporcional al tramo de vídeo que mandas procesar (el `timeframe`), así que reducir el tramo baja el coste. Cuando el saldo no cubre el envío, `POST /clips` responde `402 INSUFFICIENT_CREDITS`, y el `extra` trae `credits_needed` y `current_balance`. ## Consultando el saldo Saldo actual del workspace: ```bash curl https://api.cut.pro/api/v1/balance \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` ```json title="Respuesta" { "balance": 480, "unlimited": false, "unlimited_until": null } ``` Extracto de movimientos (créditos añadidos y consumidos): ```bash curl https://api.cut.pro/api/v1/balance/history \ -H "X-Api-Key: $CUTPRO_API_KEY" ``` > ¿Quieres prever el coste antes de enviar? Llama a `POST /clips/info` con la URL del vídeo: la respuesta trae `credits_cost`, `discount_percent` y `current_balance`. --- # Documentación CutPro > Documentación oficial de Cut.Pro: primeros pasos con la API, autenticación, créditos, publicación en redes y la referencia completa de endpoints. Convierte vídeos largos en clips verticales para TikTok, Instagram y YouTube Shorts usando inteligencia artificial, de forma programática. Tú envías un enlace, la IA elige los mejores momentos y recibes los clips listos para renderizar y publicar. [Empezar por el Quickstart](https://cut.pro/docs/es/api-reference/quickstart) [Ver todos los endpoints](https://cut.pro/docs/reference) ## La primera petición Analizar un vídeo muestra el coste en créditos antes de gastar nada. Es la llamada con la que empieza todo el flujo. **curl** ```bash 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" }' ``` **Node.js** ```js const response = await fetch("https://api.cut.pro/api/v1/clips/info", { method: "POST", headers: { "X-Api-Key": process.env.CUTPRO_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }), }); const video = await response.json(); ``` **Python** ```python response = requests.post( "https://api.cut.pro/api/v1/clips/info", headers={"X-Api-Key": os.environ["CUTPRO_API_KEY"]}, json={"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}, ) video = response.json() ``` ```json title="Respuesta" { "video_id": "7412908365112823", "title": "Entrevista completa: 2 horas sobre carrera", "author": "Canal de ejemplo", "platform": "youtube", "duration": 7245, "credits_cost": 121, "credits_original": 121, "discount_percent": 0, "current_balance": 480, "credits_unlimited": false, "force_watermark": false } ``` Con el `video_id` en la mano, `POST /clips` envía el vídeo al recorte. El [Quickstart](/docs/es/api-reference/quickstart) sigue desde ahí hasta el MP4 renderizado. ## Empieza por aquí - [Quickstart](https://cut.pro/docs/es/api-reference/quickstart): Del enlace al MP4 renderizado, con las peticiones en orden - [Autenticación](https://cut.pro/docs/es/api-reference/autenticacao): Cómo generar tu clave y elegir el workspace - [Visión general de la API](https://cut.pro/docs/es/api-reference/introducao): El flujo entero, del enlace del vídeo al clip publicado - [Referencia de la API](https://cut.pro/docs/reference): Los 44 endpoints, con ejemplos y cliente para probarlos ## Guías - [Enviando un vídeo](https://cut.pro/docs/es/api-reference/enviando-video): Por URL pública o subiendo tu propio archivo - [Workspace y créditos](https://cut.pro/docs/es/api-reference/saldo): Cómo funciona el consumo y cómo consultar el saldo - [Publicando en redes](https://cut.pro/docs/es/api-reference/postagem): Un clip en varias cuentas, con programación - [Límites de solicitudes](https://cut.pro/docs/es/api-reference/rate-limit): Cuántas llamadas por minuto y cómo tratar el 429 - [Conectar vía MCP](https://cut.pro/docs/es/api-reference/mcp): Usa CutPro dentro de Claude, ChatGPT y tu editor - [Claves de API](https://cut.pro/studio/me/api-keys): Crea y revoca claves en los ajustes de tu cuenta --- # Documentação CutPro > Documentação oficial do Cut.Pro: primeiros passos com a API, autenticação, créditos, publicação nas redes e a referência completa dos endpoints. Transforme vídeos longos em clipes verticais para TikTok, Instagram e YouTube Shorts com inteligência artificial, de forma programática. Você envia um link, a IA escolhe os melhores momentos, e você recebe os cortes prontos para renderizar e publicar. [Começar pelo Quickstart](https://cut.pro/docs/api-reference/quickstart) [Ver todos os endpoints](https://cut.pro/docs/reference) ## A primeira requisição Analisar um vídeo mostra o custo em créditos antes de você gastar qualquer coisa. É a chamada por onde todo fluxo começa. **curl** ```bash 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" }' ``` **Node.js** ```js const response = await fetch("https://api.cut.pro/api/v1/clips/info", { method: "POST", headers: { "X-Api-Key": process.env.CUTPRO_API_KEY, "Content-Type": "application/json", }, body: JSON.stringify({ url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }), }); const video = await response.json(); ``` **Python** ```python response = requests.post( "https://api.cut.pro/api/v1/clips/info", headers={"X-Api-Key": os.environ["CUTPRO_API_KEY"]}, json={"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"}, ) video = response.json() ``` ```json title="Resposta" { "video_id": "7412908365112823", "title": "Entrevista completa: 2 horas sobre carreira", "author": "Canal Exemplo", "platform": "youtube", "duration": 7245, "credits_cost": 121, "credits_original": 121, "discount_percent": 0, "current_balance": 480, "credits_unlimited": false, "force_watermark": false } ``` Com o `video_id` na mão, `POST /clips` submete o vídeo para a clipagem. O [Quickstart](/docs/api-reference/quickstart) segue daí até o MP4 renderizado. ## Comece por aqui - [Quickstart](https://cut.pro/docs/api-reference/quickstart): Do link ao MP4 renderizado, com as requisições na ordem - [Autenticação](https://cut.pro/docs/api-reference/autenticacao): Como gerar sua chave e escolher o workspace - [Visão geral da API](https://cut.pro/docs/api-reference/introducao): O fluxo inteiro, do link do vídeo ao clipe publicado - [Referência da API](https://cut.pro/docs/reference): Os 44 endpoints, com exemplos e cliente para testar ## Guias - [Enviando um vídeo](https://cut.pro/docs/api-reference/enviando-video): Por URL pública ou enviando seu próprio arquivo - [Workspace e créditos](https://cut.pro/docs/api-reference/saldo): Como o consumo funciona e como consultar o saldo - [Publicando nas redes](https://cut.pro/docs/api-reference/postagem): Um clipe em várias contas, com agendamento - [Limites de requisição](https://cut.pro/docs/api-reference/rate-limit): Quantas chamadas por minuto e como tratar o 429 - [Conectar via MCP](https://cut.pro/docs/api-reference/mcp): Use o CutPro dentro do Claude, do ChatGPT e do seu editor - [Chaves de API](https://cut.pro/studio/me/api-keys): Crie e revogue chaves nas configurações da sua conta ---