--- title: Quickstart | Chamelaion description: Go from zero to your first AI lip sync generation in under five minutes. --- This guide walks you through generating your first AI lip-synced video with the Chamelaion API. You’ll need an API key and a video + audio pair to get started. ## Prerequisites - A Chamelaion account with an API key (get one from the [Dashboard](https://app.chamelaion.com/settings/api-keys)) - A source video URL (MP4) containing a speaking person - A target audio URL (WAV or MP3) to sync the lips to ## Step 1: Get your API key Log in to your [Chamelaion Dashboard](https://app.chamelaion.com/settings/api-keys) and create a new API key. Copy it somewhere safe — you’ll need it for all API requests. Keep your API key secret. Do not expose it in client-side code or public repositories. ## Step 2: Start a lip sync generation Send a POST request to `/v1/lipsync/generate` with your video and audio URLs: ### Using cURL Terminal window ``` curl -X POST https://api.chamelaion.com/api/v1/lipsync/generate \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "reference_id": "my-first-lipsync", "inputs": [ { "type": "video", "url": "https://example.com/source-video.mp4" }, { "type": "audio", "url": "https://example.com/target-audio.wav" } ] }' ``` ### Using Python ``` import requests API_KEY = "YOUR_API_KEY" BASE_URL = "https://api.chamelaion.com/api" response = requests.post( f"{BASE_URL}/v1/lipsync/generate", headers={ "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", }, json={ "reference_id": "my-first-lipsync", "inputs": [ {"type": "video", "url": "https://example.com/source-video.mp4"}, {"type": "audio", "url": "https://example.com/target-audio.wav"}, ], }, ) data = response.json() print(f"Request ID: {data['request_id']}") print(f"Status: {data['status']}") ``` ### Using TypeScript ``` const API_KEY = "YOUR_API_KEY"; const BASE_URL = "https://api.chamelaion.com/api"; const response = await fetch(`${BASE_URL}/v1/lipsync/generate`, { method: "POST", headers: { Authorization: `Bearer ${API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ reference_id: "my-first-lipsync", inputs: [ { type: "video", url: "https://example.com/source-video.mp4" }, { type: "audio", url: "https://example.com/target-audio.wav" }, ], }), }); const data = await response.json(); console.log(`Request ID: ${data.request_id}`); console.log(`Status: ${data.status}`); ``` You’ll get back a response like: ``` { "status": "success", "request_id": "6f82a2d8-a6d4-4e8a-a0fa-e8b09823a2d8" } ``` ## Step 3: Poll for the result Lip sync generation is asynchronous. Use the `request_id` from Step 2 to check on your job: Terminal window ``` curl https://api.chamelaion.com/api/v1/lipsync/requests/6f82a2d8-a6d4-4e8a-a0fa-e8b09823a2d8 \ -H "Authorization: Bearer YOUR_API_KEY" ``` The response includes the current status and, when complete, the output URL: ``` { "id": "6f82a2d8-a6d4-4e8a-a0fa-e8b09823a2d8", "reference_id": "my-first-lipsync", "status": "completed", "created_at": "2026-04-07T10:00:00Z", "started_at": "2026-04-07T10:00:05Z", "finished_at": "2026-04-07T10:01:30Z", "output_url": "https://storage.chamelaion.com/output/6f82a2d8.mp4" } ``` Typical generation times are 1–3 minutes for videos under 60 seconds. We recommend polling every 5–10 seconds. ## Step 4: Download your result Once the status is `completed`, the `output_url` field contains a link to your lip-synced video. Download it or stream it directly. Terminal window ``` curl -o result.mp4 "https://storage.chamelaion.com/output/6f82a2d8.mp4" ``` ## Complete Python example Here’s a full end-to-end script that submits a job and waits for the result: ``` import requests import time API_KEY = "YOUR_API_KEY" BASE_URL = "https://api.chamelaion.com/api" HEADERS = {"Authorization": f"Bearer {API_KEY}"} # 1. Submit the lip sync job response = requests.post( f"{BASE_URL}/v1/lipsync/generate", headers={**HEADERS, "Content-Type": "application/json"}, json={ "reference_id": "quickstart-demo", "inputs": [ {"type": "video", "url": "https://example.com/video.mp4"}, {"type": "audio", "url": "https://example.com/audio.wav"}, ], }, ) response.raise_for_status() request_id = response.json()["request_id"] print(f"Submitted job: {request_id}") # 2. Poll until complete while True: status_response = requests.get( f"{BASE_URL}/v1/lipsync/requests/{request_id}", headers=HEADERS, ) status_response.raise_for_status() result = status_response.json() print(f"Status: {result['status']}") if result["status"] == "completed": print(f"Output URL: {result['output_url']}") break elif result["status"] == "failed": print(f"Error: {result.get('error_message', 'Unknown error')}") break time.sleep(5) ``` ## Next steps - Learn about [Authentication](/guides/authentication/index.md) options - Explore [Lip Sync Generation](/guides/generate-lipsync/index.md) in detail, including file uploads - Understand [Request Polling](/guides/polling-status/index.md) patterns - Handle [Errors](/guides/error-handling/index.md) gracefully