Guides
Face Swap Guide
Complete tutorial for swapping faces in images and videos with the AirCube API.
This guide walks through the complete face swap workflow: detecting faces in a source image, swapping a face into a target, and retrieving the result.
How it works
Face swap uses a three-step process:
- Detect — upload a source image and get back cropped face URLs (free).
- Swap — send a cropped face and a target image/video to perform the swap (paid).
- Poll — retrieve the result using the standard status endpoint.
Step 1: Detect faces
Send a source image containing the face you want to use. The API detects all faces and returns cropped URLs.
curl -X POST https://aircube.ai/api/v3/aircube/face-swap/detect \
-H "Authorization: Bearer $AIRCUBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"source_image": "https://example.com/portrait.jpg"}'Response:
{
"success": true,
"data": {
"faces": [
"https://assets.aircube.dev/cropped-face-1.jpg",
"https://assets.aircube.dev/cropped-face-2.jpg"
]
}
}If no faces are detected, the faces array will be empty. Verify that the source image contains a clearly visible face before proceeding.
Step 2: Swap face
Choose one of the detected face URLs and provide a target image or video to swap into.
curl -X POST https://aircube.ai/api/v3/aircube/face-swap \
-H "Authorization: Bearer $AIRCUBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source_face_url": "https://assets.aircube.dev/cropped-face-1.jpg",
"target_media": "https://example.com/group-photo.jpg"
}'Response (202 Accepted):
{
"success": true,
"data": {
"id": "gen_xyz789abc123",
"status": "processing",
"model": "Face Swap",
"type": "image",
"output_url": null,
"created_at": "2025-01-15T13:45:00.000Z"
}
}Step 3: Poll for result
curl https://aircube.ai/api/v3/status/gen_xyz789abc123 \
-H "Authorization: Bearer $AIRCUBE_API_KEY"Complete example
Python
import os
import time
import requests
API_KEY = os.environ["AIRCUBE_API_KEY"]
BASE_URL = "https://aircube.ai/api/v3"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
# Step 1: Detect
detect_res = requests.post(
f"{BASE_URL}/aircube/face-swap/detect",
headers=HEADERS,
json={"source_image": "https://example.com/portrait.jpg"},
)
faces = detect_res.json()["data"]["faces"]
if not faces:
print("No faces detected in the source image")
exit()
print(f"Detected {len(faces)} face(s), using the first one")
# Step 2: Swap
swap_res = requests.post(
f"{BASE_URL}/aircube/face-swap",
headers=HEADERS,
json={
"source_face_url": faces[0],
"target_media": "https://example.com/target-video.mp4",
},
)
generation_id = swap_res.json()["data"]["id"]
print(f"Swap submitted: {generation_id}")
# Step 3: Poll
while True:
result = requests.get(f"{BASE_URL}/status/{generation_id}", headers=HEADERS).json()
status = result["data"]["status"]
print(f"Status: {status}")
if status == "completed":
print(f"Result: {result['data']['output_url']}")
break
elif status == "failed":
print("Face swap failed")
break
time.sleep(5)JavaScript
const API_KEY = process.env.AIRCUBE_API_KEY;
const BASE_URL = "https://aircube.ai/api/v3";
const headers = {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
};
// Step 1: Detect
const detectRes = await fetch(`${BASE_URL}/aircube/face-swap/detect`, {
method: "POST",
headers,
body: JSON.stringify({
source_image: "https://example.com/portrait.jpg",
}),
});
const { data: detectData } = await detectRes.json();
if (detectData.faces.length === 0) {
console.log("No faces detected");
process.exit();
}
// Step 2: Swap
const swapRes = await fetch(`${BASE_URL}/aircube/face-swap`, {
method: "POST",
headers,
body: JSON.stringify({
source_face_url: detectData.faces[0],
target_media: "https://example.com/target-video.mp4",
}),
});
const { data: swapData } = await swapRes.json();
// Step 3: Poll
while (true) {
const pollRes = await fetch(`${BASE_URL}/status/${swapData.id}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
const result = await pollRes.json();
if (result.data.status === "completed") {
console.log(`Result: ${result.data.output_url}`);
break;
} else if (result.data.status === "failed") {
throw new Error("Face swap failed");
}
await new Promise((r) => setTimeout(r, 5000));
}Input requirements
Source image
- Must contain at least one clearly visible face
- Supported formats: jpg, jpeg, png, webp
- Higher resolution faces produce better results
- Front-facing or near-front-facing angles work best
Target media
- Supported images: jpg, jpeg, png, webp, gif
- Supported videos: mp4, webm, mkv
- The target must contain at least one face to swap into
- Multiple faces in the target — the primary/largest face is swapped
Pricing
- Detection is free — you can call the detect endpoint without charge.
- Image swap has a fixed per-swap cost.
- Video swap pricing scales with the video duration.
- Credits are charged at submission. Failed swaps are automatically refunded.
See the pricing page for current rates.
Best practices
- Verify detection first — always check that the detect endpoint returns the expected face before submitting a swap.
- Use high-quality source images — clear, well-lit face photos produce the most natural results.
- Match lighting and angle — results are more convincing when the source face lighting roughly matches the target.
- Start with images — test with image targets before moving to video, which is more expensive.