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
TL;DR
- Provide a face image URL + target video URL
- Choose a duration:
4,60,120, or180seconds- Use
4for trial renders
- Use
- 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 imageinput_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 jobGET /api/status/{job_id}— Check job statusGET /api/credits/{API_KEY}— View remaining creditsGET /api/jobs/{API_KEY}— List recent jobs
Parameters for POST /api/generate:
key(string, required) — your API keyinput_image_url(string, required) — public URL to the face imageinput_video_url(string, required) — public URL to the target videoduration(int, required) — one of 4, 60, 120, 180 (use 4 for trial)gender(string, optional) —all|female|male(defaultall)
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_urlandinput_video_urlare publicly accessible without auth. - Duration vs credits:
4seconds uses trial credits;60/120/180seconds consume 1/2/3 paid credits. - Backoff on polling: Use exponential backoff (as shown) to reduce API load.
- Timeouts: Increase
timeout_sif 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.