Skip to main content
Unlike chat and images, video takes 30s to 5min depending on the model. So the flow is asynchronous with two endpoints:
POST /v1/videos/generations    → Creates job, returns job_id in ms
GET  /v1/videos/{job_id}        → Current status + video URL if completed

Available models

IDProviderPrice/secDefault durationMaxAudio
google/veo-3.1Google$0.408s8s
google/veo-3Google$0.208s8s
fal/veo-3.1fal.ai$0.408s8s
fal/runway-gen-4fal.ai$0.055s10s
fal/luma-ray-2fal.ai$0.205s9s
fal/kling-2fal.ai$0.045s10s
fal/hailuo-02fal.ai$0.046s10s

Usage pattern

import time
from openai import OpenAI

client = OpenAI(
    base_url="https://api.geekhub.mx/v1",
    api_key="ghub_sk_live_xxx",
)

# 1. Submit
job = client.post("/videos/generations", body={
    "model": "fal/kling-2",
    "prompt": "A hummingbird drinking from a cempasúchil flower at dawn",
    "duration": 5,
})
job_id = job["id"]
print(f"Job created: {job_id}")

# 2. Poll
while True:
    status = client.get(f"/videos/{job_id}")
    if status["status"] == "completed":
        print(f"Done: {status['video_url']}")
        break
    if status["status"] == "failed":
        print(f"Failed: {status['error']}")
        break
    time.sleep(15)  # Every 15 sec
Polling is lazy: the job doesn’t advance on its own (Kling/Veo process on their side). But the provider keeps working even if you don’t poll — when you do poll you’ll see the updated state.

Job states

StatusMeaning
pendingJob created in our DB, not yet sent to provider
processingProvider is rendering
completedDone. video_url points to your Supabase Storage
failedAn error occurred. error describes the cause
We only charge the balance when status reaches completed (at model price × duration × markup × FX). If the job fails, you’re not charged.

Storage

Same as images: finished videos upload to your Supabase Storage (generated-videos) with persistent public UUID-based URLs.

Next steps

POST /v1/videos/generations

Submit a new video job.

GET /v1/videos/{id}

Poll the job status.