lipsyn.cc API

Generate AI lip-sync videos programmatically. Asynchronous, minute-based pricing, webhook or polling delivery.

Getting Started: Create an API key in your account settings, then purchase prepaid minutes. Each lip-sync render consumes minutes based on video length and resolution. Manage API keys →

Authentication

All API requests require a valid API key sent via the Authorization header:

Authorization: Bearer lip_your_api_key_here

Create and manage API keys in your account dashboard. Each key shows its usage (minutes processed) and last-used timestamp. You can create multiple keys for different applications, and disable or delete them individually.

Minute-Based Pricing

Each API call consumes prepaid minutes from your account. Minutes are deducted based on the output video length and resolution:

ResolutionMultiplier
≤ 1080p (HD)1× video length
> 1080p (4K)2× video length

Purchase minute bundles from your account dashboard. Minutes never expire.

Create a Lip-Sync Video

POST https://www.lipsyn.cc/api/lipsync

JSON Request (URL-based media)

{
  "audio": {
    "uri": "https://example.com/audio.mp3"
    // OR use TTS:
    // "voice": "en-US-JennyNeural",
    // "text": "Hello, this is a lip-sync test."
  },
  "video": {
    "uri": "https://example.com/video.mp4"
    // OR use an image:
    // "imageUri": "https://example.com/photo.jpg"
    // OR use a preset:
    // "preset": "https://lipsyn.cc/presets/talking_avatar.mp4"
  },
  "webhookUrl": "https://your-app.com/webhook",
  "language": "en-US"
}

Multipart Form Data (file uploads)

POST /api/lipsync
Content-Type: multipart/form-data
Authorization: Bearer lip_your_api_key_here

Fields:
  audio       - Audio file (MP3, WAV, etc.)
  video       - Video file (MP4, MOV, etc.) OR
  image       - Image file (JPG, PNG)
  presetLink  - Preset video URL (instead of video/image)
  voice       - TTS voice name (instead of audio file)
  text        - TTS text (required with voice)
  language    - Language code (default: en-US)
  webhookUrl  - Optional webhook URL

Response (202 Accepted)

{
  "success": true,
  "videoId": "550e8400-e29b-41d4-a716-446655440000",
  "message": "Video queued for processing. Poll GET /api/video/:videoId to check status.",
  "pollingUrl": "https://www.lipsyn.cc/api/video/550e8400-e29b-41d4-a716-446655440000"
}

Polling for Results

Poll the video status endpoint to check when processing is complete:

GET https://www.lipsyn.cc/api/video/:videoId

Authorization: Bearer lip_your_api_key_here

Response

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "preview_ready",     // or "processing", "full_ready", "error"
  "videoUrl": "https://...",      // preview or full video URL
  "fullUrl": "https://...",       // full video URL (when status is "full_ready")
  "previewUrl": "https://...",    // preview URL
  "length": 15,                   // duration in seconds
  "height": 720,                  // video height in pixels
  "errorCode": null,
  "errorMessage": null
}

Status Values

  • new — Video created, waiting for processing
  • processing — Currently being processed
  • preview_ready — Preview (short version) is available at previewUrl
  • full_ready — Full video is available at fullUrl
  • error — Processing failed, check errorMessage

Poll every 2-5 seconds. Processing typically takes 30 seconds to 5 minutes depending on video length and queue depth.

Webhooks

Pass a webhookUrl in the create request to receive a POST when processing completes. The webhook payload:

POST {your_webhook_url}
Content-Type: application/json

{
  "event": "video.completed",
  "videoId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "full_ready",
  "videoUrl": "https://...",
  "fullUrl": "https://...",
  "previewUrl": "https://...",
  "length": 15,
  "height": 720
}

Your webhook endpoint should return a 200 status to acknowledge receipt. Webhooks are sent on both success and failure (status will be error).

Code Samples

cURL — Create & Poll

# Step 1: Create a lip-sync video
curl -X POST https://www.lipsyn.cc/api/lipsync \
  -H "Authorization: Bearer lip_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "audio": { "uri": "https://example.com/audio.mp3" },
    "video": { "uri": "https://example.com/video.mp4" },
    "webhookUrl": "https://your-app.com/webhook"
  }'

# Response: { "videoId": "abc-123", "pollingUrl": "..." }

# Step 2: Poll for results
curl -H "Authorization: Bearer lip_your_api_key_here" \
  https://www.lipsyn.cc/api/video/abc-123

Python — Polling

import requests, time

API_KEY = "lip_your_api_key_here"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

# Create video
resp = requests.post(
    "https://www.lipsyn.cc/api/lipsync",
    headers=HEADERS,
    json={
        "audio": {"uri": "https://example.com/audio.mp3"},
        "video": {"uri": "https://example.com/video.mp4"},
    },
)
data = resp.json()
video_id = data["videoId"]
print(f"Video queued: {video_id}")

# Poll until ready
while True:
    resp = requests.get(
        f"https://www.lipsyn.cc/api/video/{video_id}",
        headers=HEADERS,
    )
    status = resp.json()["status"]
    print(f"Status: {status}")

    if status == "full_ready":
        video_url = resp.json()["fullUrl"]
        print(f"Video ready: {video_url}")
        # Download the video
        r = requests.get(video_url)
        with open("output.mp4", "wb") as f:
            f.write(r.content)
        break
    elif status == "error":
        print(f"Error: {resp.json()['errorMessage']}")
        break

    time.sleep(3)

Node.js — Webhook Receiver

import express from 'express';
const app = express();

app.post('/webhook', express.json(), (req, res) => {
  const { event, videoId, status, videoUrl } = req.body;

  if (event === 'video.completed') {
    console.log(`Video ${videoId} is ${status}`);
    if (status === 'full_ready') {
      // Download or process the video
      console.log(`Download from: ${videoUrl}`);
    }
  }

  res.status(200).send('OK');
});

app.listen(3000);

Python — Create with Webhook

import requests

API_KEY = "lip_your_api_key_here"

resp = requests.post(
    "https://www.lipsyn.cc/api/lipsync",
    headers={"Authorization": f"Bearer {API_KEY}"},
    json={
        "audio": {"voice": "en-US-JennyNeural", "text": "Hello world!"},
        "video": {"imageUri": "https://example.com/photo.jpg"},
        "webhookUrl": "https://your-server.com/webhook",
    },
)
print(resp.json())
# Response includes videoId — you can also poll as a fallback

Python — File Upload (Multipart)

import requests

API_KEY = "lip_your_api_key_here"

with open("video.mp4", "rb") as vf, open("audio.mp3", "rb") as af:
    resp = requests.post(
        "https://www.lipsyn.cc/api/lipsync",
        headers={"Authorization": f"Bearer {API_KEY}"},
        files={
            "video": vf,
            "audio": af,
        },
        data={
            "webhookUrl": "https://your-server.com/webhook",
        },
    )
print(resp.json())

Error Handling

StatusMeaning
202Accepted — video queued for processing
400Bad request — missing or invalid parameters
401Unauthorized — invalid or missing API key
402Payment required — insufficient minute balance
413File too large — max 512 MB
429Too many requests — rate limit exceeded
500Internal server error

Rate Limits

  • 60 requests per minute per API key
  • Max file size: 512 MB per upload
  • Max video duration: 120 seconds
  • Concurrent processing: 5 videos per account

Questions? Contact support@lipsyn.cc