Polling & Request Status
How to check the status of lip sync and video translation jobs and list your request history.
CHAMELAION processes jobs asynchronously. After submitting a generation request, you’ll receive a request_id that you use to poll for the result. This guide covers how to check individual request status and list your request history.
Lip Sync and Video Translate use the same pattern on separate endpoints:
- Lip Sync:
GET /v1/lipsync/requests/{id}andGET /v1/lipsync/requests - Video Translate:
GET /v1/video-translate/requests/{id}andGET /v1/video-translate/requests
The examples below use Lip Sync. For Video Translate responses and filters, see Poll a request and List requests.
Request lifecycle
Section titled “Request lifecycle”Lip Sync requests move through queued → started → finished. Video Translate requests move through queued → uploading → processing → completed. Either can end in failed.
| Status | Product | Description |
|---|---|---|
queued | Both | Request received and waiting to be processed |
started | Lip Sync | Lip sync generation is in progress |
finished | Lip Sync | Generation finished. output_url is available |
uploading | Video Translate | The source video is being downloaded and prepared |
processing | Video Translate | Translation is in progress |
completed | Video Translate | Translation finished. outputs is available |
failed | Both | The request failed. Check error_message for details |
Get a single request
Section titled “Get a single request”Use GET /v1/lipsync/requests/{id} to check the status of a specific request. You can pass either the CHAMELAION UUID or your own reference_id.
By request ID
Section titled “By request ID”curl https://api.chamelaion.com/api/v1/lipsync/requests/6f82a2d8-a6d4-4e8a-a0fa-e8b09823a2d8 \ -H "Authorization: Bearer $CHAMELAION_API_KEY"By reference ID
Section titled “By reference ID”curl https://api.chamelaion.com/api/v1/lipsync/requests/my-custom-reference \ -H "Authorization: Bearer $CHAMELAION_API_KEY"Use unique reference IDs. If several lip sync requests share a reference_id, retrieving by that value fails with a 500 error.
Response (finished)
Section titled “Response (finished)”{ "id": "6f82a2d8-a6d4-4e8a-a0fa-e8b09823a2d8", "reference_id": "my-custom-reference", "status": "finished", "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"}Response (failed)
Section titled “Response (failed)”{ "id": "6f82a2d8-a6d4-4e8a-a0fa-e8b09823a2d8", "reference_id": "my-custom-reference", "status": "failed", "created_at": "2026-04-07T10:00:00Z", "started_at": "2026-04-07T10:00:05Z", "finished_at": "2026-04-07T10:00:12Z", "error_message": "failed to process video"}Polling pattern
Section titled “Polling pattern”Here’s the recommended polling pattern with exponential backoff. Pass video-translate as the product to poll a Video Translate request:
Python
Section titled “Python”import requestsimport timeimport os
API_KEY = os.environ["CHAMELAION_API_KEY"]BASE_URL = "https://api.chamelaion.com/api"HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def wait_for_completion(request_id: str, product: str = "lipsync", timeout: int = 600) -> dict: """Poll a request until it completes or fails.""" start = time.time() delay = 2
while time.time() - start < timeout: response = requests.get( f"{BASE_URL}/v1/{product}/requests/{request_id}", headers=HEADERS, ) response.raise_for_status() result = response.json()
if result["status"] in ("finished", "completed"): return result elif result["status"] == "failed": raise Exception(f"Generation failed: {result.get('error_message')}")
time.sleep(delay) delay = min(delay * 1.5, 15) # cap at 15 seconds
raise TimeoutError(f"Request {request_id} did not complete within {timeout}s")TypeScript
Section titled “TypeScript”async function waitForCompletion( requestId: string, product: "lipsync" | "video-translate" = "lipsync", timeoutMs = 600_000): Promise<Record<string, unknown>> { const start = Date.now(); let delay = 2000;
while (Date.now() - start < timeoutMs) { const response = await fetch( `https://api.chamelaion.com/api/v1/${product}/requests/${requestId}`, { headers: { Authorization: `Bearer ${process.env.CHAMELAION_API_KEY}` } } );
const result = await response.json();
if (result.status === "finished" || result.status === "completed") return result; if (result.status === "failed") { throw new Error(`Generation failed: ${result.error_message}`); }
await new Promise((r) => setTimeout(r, delay)); delay = Math.min(delay * 1.5, 15_000); }
throw new Error(`Request ${requestId} timed out`);}List all requests
Section titled “List all requests”Use GET /v1/lipsync/requests to retrieve a paginated list of your lip sync requests, newest first.
Basic listing
Section titled “Basic listing”curl "https://api.chamelaion.com/api/v1/lipsync/requests" \ -H "Authorization: Bearer $CHAMELAION_API_KEY"With pagination
Section titled “With pagination”curl "https://api.chamelaion.com/api/v1/lipsync/requests?limit=10&offset=20" \ -H "Authorization: Bearer $CHAMELAION_API_KEY"Filter by reference ID
Section titled “Filter by reference ID”curl "https://api.chamelaion.com/api/v1/lipsync/requests?reference_id=batch-2026-04" \ -H "Authorization: Bearer $CHAMELAION_API_KEY"Query parameters
Section titled “Query parameters”| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Number of results per page (minimum 1) |
offset | integer | 0 | Number of results to skip |
reference_id | string | None | Filter by exact reference_id |
Response
Section titled “Response”{ "data": [ { "id": "6f82a2d8-a6d4-4e8a-a0fa-e8b09823a2d8", "reference_id": "batch-2026-04", "status": "finished", "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" }, { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "reference_id": "batch-2026-04", "status": "queued", "created_at": "2026-04-07T10:02:00Z" } ], "pagination": { "limit": 20, "offset": 0, "total": 2 }}Paginating through all results
Section titled “Paginating through all results”Python
Section titled “Python”import requestsimport os
API_KEY = os.environ["CHAMELAION_API_KEY"]BASE_URL = "https://api.chamelaion.com/api"HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def list_all_requests(): """Iterate through all lip sync requests.""" offset = 0 limit = 50 all_requests = []
while True: response = requests.get( f"{BASE_URL}/v1/lipsync/requests", headers=HEADERS, params={"limit": limit, "offset": offset}, ) response.raise_for_status() data = response.json()
all_requests.extend(data["data"])
if offset + limit >= data["pagination"]["total"]: break offset += limit
return all_requestsResponse fields reference
Section titled “Response fields reference”Lip Sync requests return these fields. Video Translate requests also return their settings (source_language, target_languages, and so on) and replace output_url with outputs, keyed by target language. See the request schema for every field.
| Field | Type | Present | Description |
|---|---|---|---|
id | string | Always | CHAMELAION request UUID |
reference_id | string | If provided at creation | Your custom identifier |
status | string | Always | Current status (queued, started, finished, failed) |
created_at | datetime | Always | When the request was created (UTC) |
started_at | datetime | After processing starts | When processing began (UTC) |
finished_at | datetime | After completion/failure | When processing ended (UTC) |
output_url | string | On finished | Signed URL to the generated video, valid for one hour |
error_message | string | On failed | Description of what went wrong |