AirCubeAirCube
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:

  1. Detect — upload a source image and get back cropped face URLs (free).
  2. Swap — send a cropped face and a target image/video to perform the swap (paid).
  3. 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.

On this page