Face Swap API
Detect faces and swap them in images and videos using the AirCube API.
Face swap uses a two-step workflow: first detect faces in a source image (free), then swap the detected face into a target image or video (paid).
Step 1: Detect faces
POST https://aircube.ai/api/v3/aircube/face-swap/detectAnalyzes a source image and returns cropped face URLs. This endpoint is free — no credits are charged.
Request
{
"source_image": "https://example.com/portrait.jpg"
}| Field | Type | Required | Description |
|---|---|---|---|
source_image | string | Yes | URL of an image containing one or more faces |
Response (200 OK)
Faces detected:
{
"success": true,
"data": {
"faces": [
"https://assets.aircube.dev/cropped-face-1.jpg",
"https://assets.aircube.dev/cropped-face-2.jpg"
]
}
}No faces detected:
{
"success": true,
"data": {
"faces": [],
"message": "No faces detected in image"
}
}Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Missing source_image field |
| 500 | INTERNAL_ERROR | Face detection failed |
Step 2: Swap face
POST https://aircube.ai/api/v3/aircube/face-swapSwaps a detected face into a target image or video. This endpoint charges credits based on the target media type and duration.
Request
{
"source_face_url": "https://assets.aircube.dev/cropped-face-1.jpg",
"target_media": "https://example.com/target-video.mp4"
}| Field | Type | Required | Description |
|---|---|---|---|
source_face_url | string | Yes | A face URL returned from the detect endpoint |
target_media | string | Yes | URL of the target image, video, or GIF |
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"
}
}The type field reflects the target media type (image or video).
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_ERROR | Missing fields or invalid media URL |
| 402 | PAYMENT_REQUIRED | Insufficient credits |
| 500 | INTERNAL_ERROR | Processing failed |
Step 3: Poll for result
Use the Get Result endpoint to poll for the swap output:
GET https://aircube.ai/api/v3/status/{id}Pricing
Face swap pricing depends on the target media type:
| Target type | Pricing |
|---|---|
| Image | Fixed price per swap |
| Video | Price scales with video duration |
Credits are charged when the swap request is submitted. If the swap fails, credits are automatically refunded.
Complete examples
cURL
# Step 1: Detect faces (free)
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"}'
# Step 2: Swap face (paid)
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/target-video.mp4"
}'
# Step 3: Poll for result
curl https://aircube.ai/api/v3/status/gen_xyz789abc123 \
-H "Authorization: Bearer $AIRCUBE_API_KEY"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 faces
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")
exit()
print(f"Detected {len(faces)} face(s)")
# Step 2: Swap face
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"]
# Step 3: Poll for result
while True:
result = requests.get(
f"{BASE_URL}/status/{generation_id}",
headers=HEADERS,
).json()
status = result["data"]["status"]
if status == "completed":
print(f"Output: {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 faces
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 face
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 for result
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(`Output: ${result.data.output_url}`);
break;
} else if (result.data.status === "failed") {
throw new Error("Face swap failed");
}
await new Promise((r) => setTimeout(r, 5000));
}