Face Swap Video API

Community Article Published November 5, 2025

This guide shows how to use the face-swap.co API to create face-swapped videos.


Google Colab Notebook

Google Colab logo


TL;DR

  • Provide a face image URL + target video URL
  • Choose a duration: 4, 60, 120, or 180 seconds
    • Use 4 for trial renders
  • Receive a job_id, poll until complete, then download the final video URL

Pricing & Credits (API)

  • 1 credit = 1 minute of video
  • Trial: 5 trial credits (once per email). Trial uses duration=4 (4 seconds)
  • Paid: 10 credits – $9.99, 25 credits – $19.99

Purchase or start a free trial on face-swap.co/api. For trials, set duration=4.


Setup

  • Python 3.8+
  • Install dependency:
pip install requests
  • Prepare public URLs:
    • input_image_url — face image
    • input_video_url — target video
  • Create an API key on face-swap.co/api

Keep your key out of source code:

export FACESWAP_API_KEY="YOUR_API_KEY"

API Endpoints

Base URL: https://www.face-swap.co

  • POST /api/generate — Create a face-swap job
  • GET /api/status/{job_id} — Check job status
  • GET /api/credits/{API_KEY} — View remaining credits
  • GET /api/jobs/{API_KEY} — List recent jobs

Parameters for POST /api/generate:

  • key (string, required) — your API key
  • input_image_url (string, required) — public URL to the face image
  • input_video_url (string, required) — public URL to the target video
  • duration (int, required) — one of 4, 60, 120, 180 (use 4 for trial)
  • gender (string, optional) — all | female | male (default all)

Quickstart — Create a Job

import os
import requests

API_KEY = os.getenv("FACESWAP_API_KEY") or "YOUR_API_KEY"
url = "https://www.face-swap.co/api/generate"

payload = {
    "key": API_KEY,
    "input_image_url": "https://tinyurl.com/elonmusk-faceswap",   # replace with your face image (public URL)
    "input_video_url": "https://tinyurl.com/ironman-faceswap",    # replace with your target video (public URL)
    "duration": 120,   # required: 4 | 60 | 120 | 180  (use 4 for trial)
    "gender": "all",   # optional: all | female | male
}

r = requests.post(url, json=payload, timeout=60)
r.raise_for_status()
data = r.json()
print(data)
# Expect JSON that includes a job identifier you can poll with /api/status (e.g., "job_id").

Poll for Completion

Jobs are asynchronous. Poll until the status is terminal.

import time
import requests

def wait_for_job(job_id, *, timeout_s=900, poll_interval_s=4.0, backoff=1.3):
    """
    Polls /api/status/{job_id} until completion or timeout.
    Returns the last JSON response from the status endpoint.
    """
    status_url = f"https://www.face-swap.co/api/status/{job_id}"
    t0 = time.time()
    interval = poll_interval_s

    while True:
        r = requests.get(status_url, timeout=30)
        r.raise_for_status()
        info = r.json()
        status = str(info.get("status", "")).lower()
        print(f"status={status}")

        if status in {"done", "completed", "succeeded"}:
            return info
        if status in {"failed", "error", "canceled"}:
            return info

        if time.time() - t0 > timeout_s:
            raise TimeoutError(f"Job {job_id} not finished after {timeout_s}s; last status={status}")

        time.sleep(interval)
        interval = min(interval * backoff, 15.0)

Download the Final Video

When the status indicates success, the response usually includes a result URL to the rendered video.

import os
import requests

def download_file(url, out_path):
    os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
    with requests.get(url, stream=True, timeout=180) as r:
        r.raise_for_status()
        with open(out_path, "wb") as f:
            for chunk in r.iter_content(chunk_size=1024 * 1024):
                if chunk:
                    f.write(chunk)
    return out_path

Check Credits

import os
import requests

API_KEY = os.getenv("FACESWAP_API_KEY") or "YOUR_API_KEY"
url = f"https://www.face-swap.co/api/credits/{API_KEY}"

r = requests.get(url, timeout=30)
r.raise_for_status()
print(r.json())

List Recent Jobs

import os
import requests

API_KEY = os.getenv("FACESWAP_API_KEY") or "YOUR_API_KEY"
url = f"https://www.face-swap.co/api/jobs/{API_KEY}"

r = requests.get(url, timeout=30)
r.raise_for_status()
print(r.json())

End-to-End Example Script

Save as face_swap_run.py. It creates a job, polls until it’s done, then downloads the output video.

import os
import time
import requests
from urllib.parse import urlparse

API_KEY = os.getenv("FACESWAP_API_KEY") or "YOUR_API_KEY"

GENERATE_URL = "https://www.face-swap.co/api/generate"
STATUS_URL   = "https://www.face-swap.co/api/status/{job_id}"

FACE_IMAGE_URL   = os.getenv("FS_FACE_URL")  or "https://tinyurl.com/elonmusk-faceswap"
TARGET_VIDEO_URL = os.getenv("FS_VIDEO_URL") or "https://tinyurl.com/ironman-faceswap"
DURATION         = int(os.getenv("FS_DURATION", "60"))   # 4|60|120|180
GENDER           = os.getenv("FS_GENDER", "all")         # all|female|male

def create_job():
    payload = {
        "key": API_KEY,
        "input_image_url": FACE_IMAGE_URL,
        "input_video_url": TARGET_VIDEO_URL,
        "duration": DURATION,
        "gender": GENDER,
    }
    r = requests.post(GENERATE_URL, json=payload, timeout=60)
    r.raise_for_status()
    return r.json()

def poll_status(job_id, timeout_s=900):
    url = STATUS_URL.format(job_id=job_id)
    t0 = time.time()
    interval = 4.0
    while True:
        r = requests.get(url, timeout=30)
        r.raise_for_status()
        data = r.json()
        status = str(data.get("status", "")).lower()
        print(f"[{time.strftime('%H:%M:%S')}] status={status}")

        if status in {"done", "completed", "succeeded"}:
            return data
        if status in {"failed", "error", "canceled"}:
            return data

        if time.time() - t0 > timeout_s:
            raise TimeoutError(f"Timed out after {timeout_s}s waiting for job {job_id}")

        time.sleep(interval)
        interval = min(interval * 1.3, 15.0)

def download(url, out_dir="outputs"):
    os.makedirs(out_dir, exist_ok=True)
    name = os.path.basename(urlparse(url).path) or "faceswap.mp4"
    path = os.path.join(out_dir, name)
    with requests.get(url, stream=True, timeout=180) as r:
        r.raise_for_status()
        with open(path, "wb") as f:
            for chunk in r.iter_content(1024 * 1024):
                if chunk:
                    f.write(chunk)
    return path

if __name__ == "__main__":
    job = create_job()
    job_id = job.get("job_id") or job.get("id") or job.get("uuid")
    if not job_id:
        raise SystemExit(f"Could not find job id in response: {job}")

    print(f"Created job: {job_id}")
    final = poll_status(job_id)
    print("Final status response:", final)

    if str(final.get("status", "")).lower() in {"done", "completed", "succeeded"}:
        result_url = final.get("result_url") or final.get("video_url") or final.get("output_url")
        if not result_url:
            raise SystemExit("Job succeeded but no result URL found.")
        path = download(result_url)
        print(f"Downloaded to: {path}")
    else:
        raise SystemExit("Job did not complete successfully.")

Tips & Gotchas

  • Public URLs only: Make sure input_image_url and input_video_url are publicly accessible without auth.
  • Duration vs credits: 4 seconds uses trial credits; 60/120/180 seconds consume 1/2/3 paid credits.
  • Backoff on polling: Use exponential backoff (as shown) to reduce API load.
  • Timeouts: Increase timeout_s if you expect longer renders.
  • Email delivery: If you also kick off renders via the website, finished links are emailed automatically; the API gives you JSON status directly.

Community

Sign up or log in to comment