Skip to content
DashboardGet API Key
Core Guides

Error Handling

Understanding error responses, HTTP status codes, rate limits, and troubleshooting common issues.

The Chamelaion API uses standard HTTP status codes and returns consistent JSON error responses. This guide covers all error scenarios, rate limiting, and how to handle them in your application.

All errors follow the same structure:

{
"error": "human-readable error message"
}

The error field always contains a descriptive message explaining what went wrong.

StatusMeaningDescription
200SuccessRequest completed successfully
400Bad RequestInvalid request body, missing fields, or wrong format
401UnauthorizedAuthentication failed or missing
404Not FoundThe requested lip sync job doesn’t exist
412Precondition FailedToken limit reached — upgrade your plan
429Too Many RequestsRate limit exceeded — slow down
500Internal Server ErrorSomething went wrong on our side

These indicate problems with your request format or content.

URL-based generation (/v1/lipsync/generate)

Section titled “URL-based generation (/v1/lipsync/generate)”
Error MessageCause
invalid request bodyJSON is malformed or doesn’t match the schema
content-type must be application/jsonMissing or wrong Content-Type header
expected exactly one video inputinputs doesn’t contain exactly one video item
expected exactly one audio inputinputs doesn’t contain exactly one audio item
input type must be "video" or "audio"An input has an unrecognized type value

File upload (/v1/lipsync/generate-with-media)

Section titled “File upload (/v1/lipsync/generate-with-media)”
Error MessageCause
content-type must be multipart/form-dataWrong content type — must be multipart/form-data
video file is requiredNo video field in the form data
audio file is requiredNo audio field in the form data
model must be "lipsync-2"Invalid model name specified
import requests
response = requests.post(
"https://api.chamelaion.com/api/v1/lipsync/generate",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json={"inputs": []}, # Invalid: empty inputs
)
if response.status_code == 400:
error = response.json()
print(f"Bad request: {error['error']}")
# Bad request: expected exactly one video input

See the Authentication guide for details. Common causes:

Error MessageFix
missing authorization headerAdd Authorization: Bearer <token> or x-api-key
invalid authorization formatUse Bearer <token> format (note the space after Bearer)
missing tokenToken value is empty
invalid tokenToken is expired, revoked, or incorrect

Returned when you request a lip sync job that doesn’t exist:

{
"error": "lipsync request not found"
}

Double-check the request_id or reference_id you’re using. Common causes:

  • Typo in the ID
  • The request belongs to a different account
  • Using a reference_id that was never set

Your account’s generation quota has been exhausted:

{
"error": "token limit reached"
}

Upgrade your plan from the Dashboard to increase your quota.

You’ve exceeded the request rate limit:

{
"error": "rate limit exceeded"
}
import requests
import time
def api_request_with_retry(method, url, max_retries=3, **kwargs):
"""Make an API request with automatic retry on rate limits."""
for attempt in range(max_retries + 1):
response = requests.request(method, url, **kwargs)
if response.status_code == 429:
wait_time = 2 ** attempt # 1s, 2s, 4s
print(f"Rate limited. Retrying in {wait_time}s...")
time.sleep(wait_time)
continue
return response
raise Exception("Max retries exceeded due to rate limiting")
async function apiRequestWithRetry(
url: string,
options: RequestInit,
maxRetries = 3
): Promise<Response> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const waitTime = 2 ** attempt * 1000;
console.log(`Rate limited. Retrying in ${waitTime}ms...`);
await new Promise((r) => setTimeout(r, waitTime));
continue;
}
return response;
}
throw new Error("Max retries exceeded due to rate limiting");
}

These indicate a problem on our side:

{
"error": "internal error"
}

If you encounter 500 errors:

  1. Retry the request after a short delay
  2. If the error persists, check the status page
  3. Contact support at support@chamelaion.com with the request details

Here’s a robust Python function that handles all error types:

import requests
import time
import os
class ChamelaionError(Exception):
def __init__(self, status_code: int, message: str):
self.status_code = status_code
self.message = message
super().__init__(f"[{status_code}] {message}")
class RateLimitError(ChamelaionError):
pass
class AuthenticationError(ChamelaionError):
pass
class QuotaExceededError(ChamelaionError):
pass
def chamelaion_request(method: str, path: str, max_retries: int = 3, **kwargs):
"""Make a request to the Chamelaion API with full error handling."""
base_url = "https://api.chamelaion.com/api"
headers = {"Authorization": f"Bearer {os.environ['CHAMELAION_API_KEY']}"}
kwargs.setdefault("headers", {}).update(headers)
for attempt in range(max_retries + 1):
response = requests.request(method, f"{base_url}{path}", **kwargs)
if response.status_code == 200:
return response.json()
error_msg = response.json().get("error", "unknown error")
if response.status_code == 429:
if attempt < max_retries:
wait = 2 ** attempt
time.sleep(wait)
continue
raise RateLimitError(429, error_msg)
if response.status_code == 401:
raise AuthenticationError(401, error_msg)
if response.status_code == 412:
raise QuotaExceededError(412, error_msg)
if response.status_code == 500 and attempt < max_retries:
time.sleep(2 ** attempt)
continue
raise ChamelaionError(response.status_code, error_msg)
My generation is stuck in “queued” status

Jobs may stay in queued status during high-traffic periods. If a job remains queued for more than 10 minutes, contact support. Make sure your polling timeout is at least 10 minutes for production workloads.

The output video has no lip movement

This can happen if Chamelaion cannot detect a face in the video. Ensure your video has a clearly visible, front-facing speaker. For multi-person videos, make sure disable_active_speaker_detection is false so the correct speaker is identified.

My URLs return “video URL is not accessible”

Chamelaion’s servers need to download your media. Ensure:

  • The URLs are publicly accessible (no authentication required)
  • The URLs don’t have IP restrictions or geo-blocking
  • The URLs return the actual media content (not an HTML page)
  • Pre-signed URLs haven’t expired
I keep getting 401 errors
  • Verify your API key in the Dashboard
  • Check that you’re using the correct header format (Bearer <token> or x-api-key: <token>)
  • Make sure there’s no trailing whitespace in your token
  • Confirm the key hasn’t been revoked