Skip to main content
GET
/
v1
/
videos
/
{job_id}
GET /v1/videos/{id}
curl --request GET \
  --url https://api.geekhub.mx/v1/videos/{job_id}

Path params

job_id
string
required
El id que recibiste al crear el job (formato UUID).

Response

Cuando aún está procesando:
{
  "id": "e8c96506-72a1-429f-a7ed-4bb0a42541be",
  "object": "video.job",
  "status": "processing",
  "model": "fal/kling-2",
  "prompt": "...",
  "video_url": null,
  "duration": 5,
  "error": null,
  "created": 1782415166,
  "completed": null
}
Cuando termina (completed):
{
  "id": "e8c96506-72a1-429f-a7ed-4bb0a42541be",
  "object": "video.job",
  "status": "completed",
  "model": "fal/kling-2",
  "prompt": "...",
  "video_url": "https://supabase.geekhub.mx/storage/v1/object/public/generated-videos/<org>/<job>/video.mp4",
  "duration": 5,
  "error": null,
  "created": 1782415166,
  "completed": 1782415954
}
Cuando falla:
{
  "status": "failed",
  "video_url": null,
  "error": "fal status: ERROR",
  "completed": 1782415954
}

Cuánto polear

Cada provider tiene latencia distinta. Recomendación práctica:
ModeloPolea cadaTiempo típico total
Kling 215s2-5 min
Hailuo 0215s2-4 min
Runway Gen-410s30s-2 min
Luma Ray 210s1-3 min
Veo 3.1 (con audio)30s3-5 min
Veo 320s2-4 min
Si polea más rápido que el provider procesa, simplemente verás status: "processing" repetido — no es un error.

Patrón TS

const job = await fetch("https://api.geekhub.mx/v1/videos/generations", {
  method: "POST",
  headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
  body: JSON.stringify({ model: "fal/kling-2", prompt: "...", duration: 5 }),
}).then(r => r.json());

const jobId = job.id;

while (true) {
  const status = await fetch(`https://api.geekhub.mx/v1/videos/${jobId}`, {
    headers: { Authorization: `Bearer ${apiKey}` },
  }).then(r => r.json());

  if (status.status === "completed") {
    console.log("Done:", status.video_url);
    break;
  }
  if (status.status === "failed") {
    console.error("Failed:", status.error);
    break;
  }
  await new Promise(r => setTimeout(r, 15000)); // 15s
}

Storage

Cuando termina, descargamos el video del provider y lo subimos a tu Supabase Storage. La video_url es nuestra, no expira y es accesible públicamente con URL UUID (mismo modelo que imágenes). Si el upload a Storage falla por alguna razón, fallback transparente: te regresamos la URL del provider directo (esa sí expira en ~1h-24h dependiendo del provider).