Generate lip-sync videos programmatically with our powerful REST API
Manage your API authentication keys
Give your API key a memorable name (optional)
Login Required
Please log in to create and manage API keys.
Get started with flexible and competitive pricing plans
Note: API credits and user credits operate independently and are not interchangeable.
Secure your API requests
Include your API key as an Authorization header in all requests:
Authorization: Bearer sk_XXXX_YYYY
RESTful API endpoints
/api/v1/lipsync-videoVideo-to-video lipsync (replace audio in existing video)
/api/v1/lipsync-imageImage + audio → single-speaker talking avatar
/api/v1/talking-avatarImage + audio + prompt -> expression and motion controlled talking avatar
/api/v1/lipsync-image-multiImage + dual audio → multi-speaker avatar (conversation/dialogue)
/api/v1/jobs/{requestId}Query job status and retrieve result
Input, privacy, model coverage, and production notes
The public API currently accepts media URLs in JSON. Direct multipart file upload and asset-upload endpoints are not available yet. Signed or temporary URLs are supported as long as our workers and the generation provider can fetch them while the job is running.
API jobs are stored with private visibility. The web Public toggle does not apply to API requests, and there is currently no API parameter to publish a generation.
Select the workflow by endpoint: lipsync-image, lipsync-video, or lipsync-image-multi. Use talking-avatar for the expression and motion control image model.
This page is the current API reference. A machine-readable OpenAPI/Swagger specification is not currently published.
Aspect ratio, guidance scale, and audio guidance are not exposed as API parameters for the current lip-sync endpoints.
Submit one API request per generation. For large production batches, keep input URLs valid until completion and contact support before running very high concurrency.
Complete endpoint specifications
All endpoints share a common structure with optional webhook parameter and model-specific formState parameters.
Replace the audio in an existing video with new audio while maintaining lip-sync
webhookoptionalYour callback URL (HTTPS). We POST the result here when complete.
formStaterequiredObject containing all generation parameters (see below).
videorequiredPublic URL to source video (MP4, MOV, etc.)
audiorequiredPublic URL to speech/replacement audio (MP3, WAV, etc.)
resolutionrequired"360p", "480p", "720p", "1080p", "2k", or "4k"
mask_imageoptionalPublic URL to mask image (constrains lip-sync area to masked region)
promptoptionalText prompt for style guidance
seedoptionalInteger random seed for reproducibility
Choose how to receive your results
All API calls are asynchronous. You have two options to retrieve results:
For 1080p, 2k, and 4k jobs, webhook and polling responses return only the final upscaled video result.
Provide a webhook URL in the JSON body. We will POST the result to your endpoint when the job completes.
Webhook URLs must be public HTTPS URLs. If delivery fails, we retry up to 5 times with exponential backoff.
Omit the webhook parameter and use the returned requestId to poll GET /api/v1/jobs/{requestId} every 5-10 seconds until status is 'completed'.
POST /api/v1/lipsync-image
Authorization: Bearer sk_XXXX_YYYY
Content-Type: application/json
{
"webhook": "https://your-app.example.com/webhooks/lipsync",
"formState": {
"image": "https://.../face.png",
"audio": "https://.../voice.mp3",
"resolution": "720p",
"seed": 42
}
}
// Immediate Response:
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"message": "Task submitted successfully. Use requestId to query job status."
}
// Later, when job completes, we POST to your webhook:
POST https://your-app.example.com/webhooks/lipsync
Content-Type: application/json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"model": "lipsync-image",
"status": "completed",
"output": "https://.../result.mp4",
"error": "",
"executionTime": 12345,
"timings": null
}POST /api/v1/talking-avatar
Authorization: Bearer sk_XXXX_YYYY
Content-Type: application/json
{
"webhook": "https://your-app.example.com/webhooks/lipsync",
"formState": {
"image": "https://.../character.png",
"audio": "https://.../speech.mp3",
"prompt": "Warm presentation style, natural facial expression, subtle head motion",
"resolution": "1080p"
}
}
// Completed webhook or polling response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"model": "talking-avatar",
"status": "completed",
"output": "https://.../result.mp4",
"error": "",
"executionTime": 12345,
"timings": null
}// Step 1: Submit job (no webhook parameter)
POST /api/v1/lipsync-image
Authorization: Bearer sk_XXXX_YYYY
Content-Type: application/json
{
"formState": {
"image": "https://.../face.png",
"audio": "https://.../voice.mp3",
"resolution": "720p"
}
}
// Immediate Response:
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing",
"message": "Task submitted successfully. Use requestId to query job status."
}
// Step 2: Poll for status (repeat every 5-10 seconds)
GET /api/v1/jobs/550e8400-e29b-41d4-a716-446655440000
Authorization: Bearer sk_XXXX_YYYY
// Response while processing:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"model": "lipsync-image",
"status": "processing",
"output": null,
"error": "",
"executionTime": null,
"timings": null
}
// Response when completed:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"model": "lipsync-image",
"status": "completed",
"output": "https://.../result.mp4",
"error": "",
"executionTime": 12345,
"timings": null
}
// Response if failed:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"model": "lipsync-image",
"status": "failed",
"output": null,
"error": "Generation failed. Please try again later.",
"executionTime": null,
"timings": null
}Ready-to-use integration examples
import fetch from 'node-fetch';
import express from 'express';
const API_KEY = 'Bearer sk_XXXX_YYYY';
const BASE_URL = 'https://lipsync.studio/api/v1';
// Set up webhook server to receive results
const app = express();
app.use(express.json());
app.post('/webhooks/lipsync', (req, res) => {
const { id, status, output, error, model } = req.body;
console.log('Job update received.');
console.log('Job ID:', id);
console.log('Model:', model);
console.log('Status:', status);
if (status === 'completed') {
console.log('Video URL:', output);
} else if (status === 'failed') {
console.error('Error:', error);
}
// TODO: Save output URL to your database, send notification, etc.
res.status(200).json({ received: true });
});
app.listen(3000, () => {
console.log('Webhook server listening on port 3000');
});
// Submit job with webhook
async function submitJobWithWebhook() {
const webhookUrl = 'https://your-public-domain.com/webhooks/lipsync';
const res = await fetch(`${BASE_URL}/lipsync-image`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': API_KEY
},
body: JSON.stringify({
webhook: webhookUrl,
formState: {
image: 'https://example.com/portrait.jpg',
audio: 'https://example.com/speech.mp3',
resolution: '720p'
}
})
});
const data = await res.json();
console.log('Job submitted:', data.requestId);
console.log('Waiting for webhook callback...');
// You don't need to poll! The result will be POSTed to your webhook.
}
submitJobWithWebhook();