# Renderiza y descarga el video

> Aplica una plantilla, genera el MP4 final, sigue el render y descarga un archivo o cientos en un zip.

El clip que corta la IA es crudo. El render convierte el clip en un MP4 vertical con plantilla, subtítulos y efectos, listo para publicar.

```text
apply_template (opcional)  →  POST .../render  →  GET /renders/{id}  →  GET /renders/{id}/download
      edición                   202 queued          hasta completed         URL de 1 hora
```

## Aplica una plantilla

Las plantillas son ediciones guardadas en Cut.Pro: encuadre, estilo de subtítulos, efectos. Lista las tuyas:

```bash
curl "https://api.cut.pro/api/v1/templates?filter=mine" \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

Cada elemento trae `id`, `name`, `aspect_ratio` y `auto_add_captions`. `filter=public` lista las de Cut.Pro.

Para editar de una vez los clips de un envío:

```bash
curl -X POST https://api.cut.pro/api/v1/clips/7412908365112823/submissions/7412908371004912/apply_template \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "template_id": "7398110254410021", "clip_ids": ["7412908412887301"] }'
```

```json title="202 Accepted"
{ "clips_count": 1 }
```

Sin `clip_ids`, vale para todos los clips. Corre en segundo plano: cada clip recibe `edit_id` en `GET .../clips` cuando termina. Un lote por cuenta a la vez; otro responde `429 BATCH_ALREADY_RUNNING`.

Más simple: envía `template_id` ya en `POST /clips` y todo clip sale editado.

## Renderiza un clip

```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="202 Accepted"
{
  "id": "7412910087712256",
  "edit_id": "7412910087700001",
  "status": "queued",
  "has_watermark": false,
  "output_resolution": "1080p",
  "from_cache": false,
  "download_url": null
}
```

Con plantilla aplicada, el render usa la edición. Sin plantilla, usa el diseño predeterminado. Guarda el `edit_id`: es el que publica el video y el que genera un archivo nuevo después.

Si un render idéntico ya está listo, la respuesta es `200` con `from_cache: true` y `download_url` completo. No hay cola ni costo de espera. Pedir el mismo clip mientras se renderiza responde `409 RENDER_ALREADY_IN_PROGRESS`.

El clip de una transmisión en vivo tiene su propia ruta: [`POST /monitors/{id}/broadcasts/{broadcastId}/clips/{clipId}/render`](/es/monitoring#obtén-los-clips-de-una-transmisión-en-vivo).

## Sigue el render

```bash
curl https://api.cut.pro/api/v1/renders/7412910087712256 \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

Consulta cada 5 a 10 segundos. `progress` va de 0 a 100, útil para una barra de progreso.

| `status` | Significa |
|---|---|
| `waiting_quality` | Esperando que la fuente esté disponible en la mejor calidad. Sigue solo, sin nueva llamada. |
| `queued` | En la cola. El plan define cuántos renders corren al mismo tiempo. |
| `active` | Renderizando. |
| `completed` | Listo para descargar. |
| `failed` | Falló. Pídelo de nuevo con `POST /renders`. |
| `cancelled` | Cancelado con `DELETE`. |
| `expired` | El archivo salió del storage. La edición sigue: renderiza de nuevo. |

## Descarga el MP4

```bash
curl https://api.cut.pro/api/v1/renders/7412910087712256/download \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

```json title="200 OK"
{ "url": "https://...", "filename": "El estribillo que todo el mundo conoce.mp4" }
```

> La URL dura 1 hora. Pide una nueva en cada descarga en lugar de guardarla. El archivo queda en el storage durante el tiempo de `render_expiry_hours` del plan; después, la ruta responde `410 RENDER_FILE_EXPIRED`.

## Renderiza de nuevo

La edición nunca expira. Para tener el MP4 de un clip de hace meses, renderiza el `edit_id`:

```bash
curl -X POST https://api.cut.pro/api/v1/renders \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "edit_id": "7412910087700001" }'
```

Vale el `edit_id` de un render, de un clip o de un elemento de post. La respuesta tiene el mismo formato que al renderizar un clip.

## Descarga varios en un zip

**curl**

```bash
curl -X POST https://api.cut.pro/api/v1/renders/bulk-download \
  -H "X-Api-Key: $CUTPRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "render_ids": ["7412910087712256", "7412910087712257"] }'

curl https://api.cut.pro/api/v1/renders/bulk-download/$JOB_ID \
  -H "X-Api-Key: $CUTPRO_API_KEY"
```

**Node.js**

```js
const headers = { "X-Api-Key": process.env.CUTPRO_API_KEY, "Content-Type": "application/json" };
const API = "https://api.cut.pro/api/v1";

const { id } = await fetch(`${API}/renders/bulk-download`, {
	method: "POST",
	headers,
	body: JSON.stringify({ render_ids: renderIds }),
}).then((response) => response.json());

let job;
do {
	await new Promise((resolve) => setTimeout(resolve, 5000));
	job = await fetch(`${API}/renders/bulk-download/${id}`, { headers }).then((response) => response.json());
} while (job.status === "queued" || job.status === "active");

if (job.status === "failed") throw new Error(job.error_code);
console.log(job.download_url);
```

- Hasta 500 renders por zip. Más que eso responde `400 TOO_MANY_RENDERS`.
- Los ids de otro workspace se ignoran. Si no queda ninguno, `404 NO_VALID_RENDERS`.
- `download_url` aparece en `completed` y dura 1 hora. El zip desaparece en `expires_at`; después de eso, `410 JOB_EXPIRED`.

## Lista y elimina

| Ruta | Qué hace |
|---|---|
| `GET /renders` | Lista los renders, del más nuevo al más antiguo. Filtra con `status`: `processing`, `completed`, `failed` o `expired`. |
| `DELETE /renders/{renderId}` | Cancela un render en cola o corriendo. En un render terminado, elimina el registro y el archivo. |
| `GET /renders/limits` | Renders corriendo y en cola, resolución, marca de agua y retención del plan. Ver [Límites](/es/rate-limits#renders). |

## Errores

| Code | Cuándo |
|---|---|
| `CLIP_RESULT_NOT_FOUND` | El clip no existe en este envío. |
| `EDIT_NOT_FOUND` | El `edit_id` no es de este workspace. |
| `RENDER_ALREADY_IN_PROGRESS` | El mismo clip o edición ya se está renderizando. Sigue el render existente. |
| `RENDER_FILE_EXPIRED` | El MP4 salió del storage. Renderiza el `edit_id` de nuevo. |
| `RENDER_FAILED`, `RENDER_SETUP_FAILED` | Falla nuestra al encolar. Inténtalo de nuevo. |
