Uygulamanıza video üretimi eklemek, görsel (image) üretimi eklemekle aynı değildir. API çağrısı anında döner — ancak video henüz hazır olmaz. Size bir görev kimliği (task ID) verilir ve bitene kadar “bitti mi?” diye sormaya devam etmeniz gerekir.
Çoğu geliştirici bir video API’sini ilk kez çağırdığında, yanıt gövdesinde bir video URL’si bekler ve bunun yerine bir görev kimliği alır. Bu kılavuz, tam akışı adım adım ele alır: bir görevi gönderme, sonuçları yoklama, hataları yönetme ve URL süresi dolmadan çıktıyı depolama.
Neler geliştireceksiniz
Bir metin istemi veya görsel kabul eden, bir video üretim görevini gönderen, tamamlanana kadar yoklama yapan ve nihai video URL’sini döndüren bir arka uç (backend) servis. Dört modelle çalışacaksınız — Veo 3 Fast, Sora 2, Kling Video ve Runway — hepsi tek bir API anahtarıyla.
Önkoşullar:
- Python 3.8+ veya Node.js 18+
- Bir CometAPI anahtarı
- REST API’lere temel aşinalık
Video üretiminin neden farklı olduğunu anlayın
Görsel üretiminde bir istek gönderir ve aynı yanıtta görseli geri alırsınız. Video üretimi ise eşzamansız (async) bir görev kuyruğu kullanır:
- Üretim isteğini gönderin → size bir
task_iddöner - Her birkaç saniyede bir bir durum (status) uç noktasını yoklayın
- Durum bir terminal duruma ulaştığında video URL’sini alırsınız
- Videoyu indirin ve depolayın — URL geçicidir
Video üretimini görsel üretimi gibi ele alır ve ilk yanıtta videonun gelmesini beklerseniz, isteğiniz her seferinde zaman aşımına uğrar.
Üretim ortamındaki bir web servisinde bu yoklama döngüsü, istek işleyicisinde değil bir arka plan işçisinde (Celery, Bull veya benzeri) çalışmalıdır. Aşağıdaki örnekler eşzamanlı (senkron) yoklama kullanır — betikler ve prototipler için uygundur, ancak eşzamanlı kullanıcıları yönetmek için uygun değildir.
Bir model seçin
| Model | Sağlayıcı | Maksimum süre | Fiyat (CometAPI üzerinden) | En uygun kullanım |
|---|---|---|---|---|
| Veo 3 Fast | 8 sec | $0.05/sec | Hızlı prototipleme, sosyal klipler | |
| Sora 2 | OpenAI (CometAPI model ID) | ~10 sec | $0.08/sec | Yüksek kaliteli yaratıcı kısa videolar |
| Kling Video | Kuaishou | 10 sec | $0.13–$2.64/task | Pazarlama içeriği, ayrıntılı kontrol |
| Runway Gen-3A Turbo | Runway | 5 or 10 sec | $0.32/task | Görüntüden videoya, ticari içerik |
Kaynak**: CometAPI model sayfaları, Mayıs 2026. Not: "Sora 2", CometAPI’nin model tanımlayıcısıdır — temel model ayrıntıları için onların model sayfasına bakın.
- Veo 3 Fast hem metinden videoya hem de görüntüden videoya destek verir. Saniye başına en ucuz, iyi bir başlangıç noktası.
- Sora 2, videoyla birlikte sesi de yerel olarak üretir — ayrı bir TTS adımı olmadan diyalog, ortam sesi ve efektler.
- Kling Video size
negative_prompt,cfg_scale, kamera hareketi ayarları vepromodu sunar. Dördü içinde en fazla kontrol. - Runway, CometAPI üzerinden yalnızca görüntüden videoya çalışır. Ona statik bir görsel ve bir hareket açıklaması verin; görseli canlandırır.
Bir Veo görevi gönderin
Veo, multipart/form-data kullanır. Python requests’te doğru göndermek için files= kullanın — data=dict, application/x-www-form-urlencoded gönderir ve bu aynı şey değildir:
import requestsimport osfrom dotenv import load_dotenvload_dotenv()def submit_veo_task(prompt: str, size: str = "16x9") -> str: """Submit a Veo 3 Fast text-to-video task. Returns task_id.""" api_key = os.getenv("COMETAPI_KEY") if not api_key: raise ValueError("COMETAPI_KEY environment variable is not set") response = requests.post( "https://api.cometapi.com/v1/videos", headers={"Authorization": f"Bearer {api_key}"}, files={ "prompt": (None, prompt), "model": (None, "veo3-fast"), "size": (None, size) }, timeout=30 ) response.raise_for_status() return response.json()["id"]task_id = submit_veo_task("A paper kite drifting above a wheat field on a windy afternoon")print(f"Task submitted: {task_id}")
Sonuç için yoklama yapın
import timedef poll_veo_task(task_id: str, interval: int = 10, max_wait: int = 600) -> str: """Poll until Veo task completes. Returns video URL.""" api_key = os.getenv("COMETAPI_KEY") if not api_key: raise ValueError("COMETAPI_KEY environment variable is not set") headers = {"Authorization": f"Bearer {api_key}"} url = f"https://api.cometapi.com/v1/videos/{task_id}" elapsed = 0 while elapsed < max_wait: response = requests.get(url, headers=headers, timeout=30) response.raise_for_status() result = response.json() status = result.get("status") if status == "succeeded": return result["output"][0] elif status in ("failed", "cancelled"): raise RuntimeError( f"Task {task_id} failed with status '{status}': " f"{result.get('error', 'no error detail returned')}" ) time.sleep(interval) elapsed += interval raise TimeoutError(f"Task {task_id} did not complete within {max_wait} seconds")video_url = poll_veo_task(task_id)print(f"Video ready: {video_url}")
Daha fazla kontrol için Kling Video’yu kullanın
Kling’in farklı bir uç nokta yapısı vardır ve JSON kullanır. Kling’in terminal durum dizgesinin "succeed" ( "succeeded" değil) olduğuna dikkat edin — bu, API’nin fiili yanıt biçimiyle eşleşir:
def submit_kling_task(prompt: str, duration: str = "5", mode: str = "std") -> str: """Submit a Kling text-to-video task. Returns task_id.""" api_key = os.getenv("COMETAPI_KEY") if not api_key: raise ValueError("COMETAPI_KEY environment variable is not set") response = requests.post( "https://api.cometapi.com/kling/v1/videos/text2video", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" }, json={ "model_name": "kling-v1-6", "prompt": prompt, "negative_prompt": "blurry, low quality, watermark", "cfg_scale": 0.5, "mode": mode, # "std" or "pro" "aspect_ratio": "16:9", "duration": duration # "5" or "10" }, timeout=30 ) response.raise_for_status() return response.json()["data"]["task_id"]def poll_kling_task(task_id: str, interval: int = 10, max_wait: int = 600) -> str: """Poll Kling task until complete. Returns video URL.""" api_key = os.getenv("COMETAPI_KEY") if not api_key: raise ValueError("COMETAPI_KEY environment variable is not set") headers = {"Authorization": f"Bearer {api_key}"} url = f"https://api.cometapi.com/kling/v1/videos/text2video/{task_id}" elapsed = 0 while elapsed < max_wait: response = requests.get(url, headers=headers, timeout=30) response.raise_for_status() result = response.json() status = result["data"]["task_status"] if status == "succeed": # Kling uses "succeed", not "succeeded" return result["data"]["task_result"]["videos"][0]["url"] elif status == "failed": error_detail = result.get("data", {}).get("task_result", "no detail") raise RuntimeError( f"Kling task {task_id} failed: {error_detail}" ) time.sleep(interval) elapsed += interval raise TimeoutError(f"Kling task {task_id} timed out after {max_wait}s")
Kaynak**: CometAPI Kling Video belgeleri
Runway ile statik bir görüntüyü canlandırın
Runway yalnızca görüntüden videoya çalışır. Ayrıca ekstra bir başlık (X-Runway-Version) ister:
def submit_runway_task(image_url: str, motion_prompt: str, duration: int = 5) -> str: """Submit a Runway image-to-video task. Returns task_id.""" api_key = os.getenv("COMETAPI_KEY") if not api_key: raise ValueError("COMETAPI_KEY environment variable is not set") response = requests.post( "https://api.cometapi.com/runwayml/v1/image_to_video", headers={ "Authorization": f"Bearer {api_key}", "X-Runway-Version": "2024-11-06", "Content-Type": "application/json" }, json={ "model": "gen3a_turbo", "promptImage": image_url, # must be a stable HTTPS URL "promptText": motion_prompt, "duration": duration, "ratio": "1280:720", "watermark": False }, timeout=30 ) response.raise_for_status() return response.json()["id"]def poll_runway_task(task_id: str, interval: int = 5, max_wait: int = 600) -> str: """Poll Runway task. Returns video URL when done.""" api_key = os.getenv("COMETAPI_KEY") if not api_key: raise ValueError("COMETAPI_KEY environment variable is not set") headers = { "Authorization": f"Bearer {api_key}", "X-Runway-Version": "2024-11-06" } url = f"https://api.cometapi.com/runwayml/v1/tasks/{task_id}" elapsed = 0 while elapsed < max_wait: response = requests.get(url, headers=headers, timeout=30) response.raise_for_status() result = response.json() status = result.get("status") if status == "task_not_exist": # CometAPI-specific: task is still initializing, retry after a few seconds time.sleep(interval) elapsed += interval continue elif status == "succeeded": return result["output"][0] elif status in ("failed", "cancelled"): raise RuntimeError(f"Runway task {task_id} failed: {result.get('error', 'no detail')}") time.sleep(interval) elapsed += interval raise TimeoutError(f"Runway task {task_id} timed out after {max_wait}s")
Kaynak**: CometAPI Runway belgeleri
URL’nin süresi dolmadan videoyu kaydedin
Video üretim API’lerinden alınan video URL’leri geçicidir. Dosyayı hemen indirin ve kontrol ettiğiniz bir yerde depolayın:
import requestsimport pathlibdef download_video(url: str, output_path: str) -> None: """Download video from URL to local file using streaming.""" out = pathlib.Path(output_path) if out.parent != pathlib.Path("."): out.parent.mkdir(parents=True, exist_ok=True) with requests.get(url, stream=True, timeout=60) as r: r.raise_for_status() with open(out, "wb") as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk) print(f"Saved to {output_path}")# Full flowtask_id = submit_veo_task("A timelapse of clouds moving over a city skyline")video_url = poll_veo_task(task_id)download_video(video_url, "output/city_timelapse.mp4")
Üretimde, yerel dosyaya yazma aşamasını S3, Cloudflare R2 veya seçtiğiniz depolamaya yükleme ile değiştirin. Akış (streaming) deseni aynı kalır — tüm videoyu belleğe yüklemek yerine baytları doğrudan yönlendirin.
Hataları ele alın
| Belirti | Muhtemel neden | Çözüm |
|---|---|---|
| Görev 10+ dakika boyunca queued’da kaldı | Sunucu yükü veya model kullanılamıyor | Farklı bir modelle yeniden deneyin |
| İlk Runway yoklamasında task_not_exist | Görev hâlâ başlatılıyor | 5 sn bekleyip yeniden deneyin — CometAPI davranışı |
| Hata mesajı olmadan failed | İstem içerik filtresini tetikledi | İstemi yeniden ifade edin |
| Video URL’si 403 döndürüyor | URL indirilmeden önce süresi doldu | URL’yi aldıktan hemen sonra indirin |
| 10 dakika sonra zaman aşımı | Üretim çok uzun sürdü | max_wait’i artırın veya Veo 3 Fast’e geçin |
| Kling "succeeded" değil "succeed" döndürüyor | Kling’in API’si standart dışı durum dizgesi kullanır | Bu doğrudur — yukarıdaki Kling yoklama koduna bakın |
Kaynak: CometAPI video üretim belgeleri
Node.js sürümü
Node.js 18+ yerleşik olarak fetch ve FormData içerir. Bu örnek dört modelin tamamını kapsar:
// Node.js 18+ — no extra packages neededconst API_KEY = process.env.COMETAPI_KEY;if (!API_KEY) throw new Error('COMETAPI_KEY is not set');// --- Veo 3 Fast ---async function submitVeoTask(prompt, size = '16x9') { const form = new FormData(); form.append('prompt', prompt); form.append('model', 'veo3-fast'); form.append('size', size); const res = await fetch('https://api.cometapi.com/v1/videos', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}` }, body: form }); if (!res.ok) throw new Error(`Veo submit failed: ${res.status}`); return (await res.json()).id;}async function pollVeoTask(taskId, intervalMs = 10000, maxWaitMs = 600000) { let elapsed = 0; while (elapsed < maxWaitMs) { const res = await fetch(`https://api.cometapi.com/v1/videos/${taskId}`, { headers: { 'Authorization': `Bearer ${API_KEY}` } }); if (!res.ok) throw new Error(`Poll failed: ${res.status}`); const result = await res.json(); if (result.status === 'succeeded') return result.output[0]; if (['failed', 'cancelled'].includes(result.status)) { throw new Error(`Task ${taskId} failed: ${result.error ?? 'no detail'}`); } await new Promise(r => setTimeout(r, intervalMs)); elapsed += intervalMs; } throw new Error(`Task ${taskId} timed out`);}// --- Kling Video ---async function submitKlingTask(prompt, duration = '5', mode = 'std') { const res = await fetch('https://api.cometapi.com/kling/v1/videos/text2video', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ model_name: 'kling-v1-6', prompt, negative_prompt: 'blurry, low quality, watermark', cfg_scale: 0.5, mode, aspect_ratio: '16:9', duration }) }); if (!res.ok) throw new Error(`Kling submit failed: ${res.status}`); return (await res.json()).data.task_id;}async function pollKlingTask(taskId, intervalMs = 10000, maxWaitMs = 600000) { let elapsed = 0; while (elapsed < maxWaitMs) { const res = await fetch( `https://api.cometapi.com/kling/v1/videos/text2video/${taskId}`, { headers: { 'Authorization': `Bearer ${API_KEY}` } } ); if (!res.ok) throw new Error(`Kling poll failed: ${res.status}`); const result = await res.json(); const status = result.data.task_status; if (status === 'succeed') return result.data.task_result.videos[0].url; if (status === 'failed') { throw new Error(`Kling task ${taskId} failed: ${JSON.stringify(result.data.task_result ?? 'no detail')}`); } await new Promise(r => setTimeout(r, intervalMs)); elapsed += intervalMs; } throw new Error(`Kling task ${taskId} timed out`);}// --- Runway (image-to-video) ---async function submitRunwayTask(imageUrl, motionPrompt, duration = 5) { const res = await fetch('https://api.cometapi.com/runwayml/v1/image_to_video', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'X-Runway-Version': '2024-11-06', 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'gen3a_turbo', promptImage: imageUrl, promptText: motionPrompt, duration, ratio: '1280:720', watermark: false }) }); if (!res.ok) throw new Error(`Runway submit failed: ${res.status}`); return (await res.json()).id;}async function pollRunwayTask(taskId, intervalMs = 5000, maxWaitMs = 600000) { let elapsed = 0; while (elapsed < maxWaitMs) { const res = await fetch( `https://api.cometapi.com/runwayml/v1/tasks/${taskId}`, { headers: { 'Authorization': `Bearer ${API_KEY}`, 'X-Runway-Version': '2024-11-06' } } ); if (!res.ok) throw new Error(`Runway poll failed: ${res.status}`); const result = await res.json(); const status = result.status; if (status === 'task_not_exist') { // CometAPI-specific: task still initializing await new Promise(r => setTimeout(r, intervalMs)); elapsed += intervalMs; continue; } if (status === 'succeeded') return result.output[0]; if (['failed', 'cancelled'].includes(status)) { throw new Error(`Runway task ${taskId} failed: ${result.error ?? 'no detail'}`); } await new Promise(r => setTimeout(r, intervalMs)); elapsed += intervalMs; } throw new Error(`Runway task ${taskId} timed out`);}// Usage exampleconst taskId = await submitVeoTask('A paper kite drifting above a wheat field');const videoUrl = await pollVeoTask(taskId);console.log('Video ready:', videoUrl);
Sırada ne var
Artık dört video modeli için çalışan kodunuz, hataları ele alan bir yoklama döngünüz ve üretilen içeriği kaybetmemenizi sağlayan bir indirme adımınız var.
Çoğu geliştiricinin bir sonraki karşılaştığı sorun: tek bir modeli sabitlemiş olmaları ve daha ucuz veya daha hızlı bir seçeneğe geçmek için birden çok dosyaya dokunmaları gerekmesi. Bir sonraki makale, kodunuzu yeniden yazmadan istekleri modeller arasında nasıl yönlendireceğinizi ele alıyor.
Sonraki: Kodunuzu Yeniden Yazmadan Yapay Zekâ Modelleri Arasında Nasıl Geçiş Yaparsınız
SSS
S: API yanıtında video yerine neden bir görev kimliği alıyorum?
Video üretimi eşzamansızdır — Veo, Sora, Kling ve Runway gibi modellerin oluşturması 2–5 dakika sürer. API, isteğinizin zaman aşımına uğramaması için hemen bir görev kimliği döndürür. Görev bir terminal duruma (succeeded, succeed, failed) ulaşana kadar ayrı bir durum uç noktasını yoklarsınız.
S: Oluşturulan bir video URL’si ne kadar süre geçerli kalır?
Video üretim API’lerinden gelen URL’ler geçicidir. URL’yi aldıktan hemen sonra dosyayı indirin ve kendi depolamanıza (S3, Cloudflare R2 vb.) kaydedin. URL’yi saklayıp saatler sonra çalışmasını beklemeyin.
S: Veo 3 Fast ile Kling Video arasındaki fark nedir?
Veo 3 Fast daha ucuzdur ($0.05/sec), daha hızlıdır ve çağırması daha basittir. Kling Video ise daha fazla kontrol sunar: negative_prompt, cfg_scale, kamera hareketi ayarları ve pro kalite modu. Çıktıyı ince ayarlarla yönlendirmeniz gerekiyorsa Kling’i kullanın. Hız ve düşük maliyet istiyorsanız Veo 3 Fast’i kullanın.
S: Metin istemi yerine bir görselden video üretebilir miyim?
Evet. Veo, bir input_reference dosyası geçirerek görüntüden videoya destekler. Kling, image parametresi (URL veya base64) ile /kling/v1/videos/image2video uç noktası üzerinden destekler. Runway yalnızca görüntüden videoya çalışır — CometAPI üzerinden metin-only istem kabul etmez.
S: Neden Runway ilk yoklamada task_not_exist döndürüyor?
Bu, belgelenmiş bir CometAPI davranışıdır — görev arka uçta hâlâ başlatılıyor. Birkaç saniye bekleyip yeniden deneyin. Bu bir hata değildir. Yukarıdaki yoklama kodu bunu otomatik olarak ele alır.
S: Kling neden "succeed" yerine "succeeded" kullanıyor?
Bu, Kling’in gerçek API yanıt biçimidir. Yazım hatası değildir. Veo ve Runway "succeeded" kullanır — Kling "succeed" kullanır. Birleştirilmiş bir yoklama sarmalayıcısı (wrapper) oluşturuyorsanız her iki dizgeyi de ele almanız gerekir.
S: Eşzamanlı (senkron) yoklama döngüsünü bir web sunucusunda kullanmak güvenli midir?
Hayır. Bu kılavuzdaki yoklama döngüsü, dakikalarca iş parçacığını bloke eder. Eşzamanlı kullanıcıları olan gerçek bir web servisinde, yoklamayı bir arka plan işçisinde çalıştırın (Python için Celery, Node.js için Bull). Görevi istek işleyicisinde gönderin, istemciye görev kimliğini döndürün ve video hazır olduğunda işçi istemciyi bilgilendirsin.
