Video Head Swap API Documentation
Playground
Try it on WaveSpeedAI!Instant online AI head & face swap for videos with no watermark, delivering realistic, shareable results in seconds. Ready-to-use REST inference API, best performance, no coldstarts, affordable pricing.
Features
WaveSpeedAI Video Head Swap is an advanced AI model for replacing the entire head (face + hair + outline) of a person in a video using a reference portrait. The model keeps the body, pose, and background intact while reconstructing a new, realistic head that matches the original lighting and perspective.
🎬 What this model does
- Replaces the full head region of the subject (face, hair, silhouette, accessories)
- Preserves body pose, clothing, background, and overall composition
- Adapts the new head to scene lighting, color tone, and camera angle
- Produces clean, watermark-free outputs ready for editing or publication
⚙️ Why it looks realistic
-
Full-head geometry replacement Swaps the entire head contour instead of only facial features, avoiding mismatched hairlines or distorted skull shapes.
-
Pose and expression preservation Follows the motion in the source video so head angle, gaze direction, and expression remain consistent with the original performance.
-
Lighting and color matching Automatically adjusts skin tone, shadows, and highlights so the new head blends naturally into the scene.
-
High-resolution blending Smooth transitions around hair, neck, and accessories, minimizing visible seams or flicker across frames.
💰 Pricing
> Billing duration: Input media duration is rounded up to whole seconds. The minimum billed duration is 3 seconds; shorter inputs are billed as 3 seconds. Existing per-5-second rate tables remain unchanged.
Pricing is based on video duration and output resolution, with a 3-second minimum and 120-second cap.
| Resolution | Price per second | Min charge (3 s) | Max charge (120 s) |
|---|---|---|---|
| 480p | $0.040 | $0.120 | $4.800 |
| 720p | $0.080 | $0.240 | $9.600 |
- minimum billed duration: 3 seconds
- Maximum billed duration: 120 seconds per run (longer clips are capped at 120 s)
🔧 Input Parameters
video (required)
The source video whose head you want to replace. This defines body motion, framing, and background.
face_image / head_image (required)
A clear portrait of the target identity. Frontal or three-quarter views with good lighting work best.
resolution
Output resolution for the processed video, for example:
- 480p – more affordable drafts or quick previews
- 720p – higher-quality output suitable for most publishing workflows
seed (optional)
Controls stochastic variation in generation:
-1or empty → random seed each run- Any positive integer → reproducible results for the same inputs
(Exact field name may differ between Playground and API, but behavior is identical.)
🎯 Designed For
- Creators & influencers – Turn one performance into many identities without reshooting.
- Marketing & brands – Localize or personalize talking-head content while keeping the same body and scene.
- Film, TV & post-production – Rapid previs, mockups, and concept tests for head-replacement shots.
- Privacy & compliance – Replace real heads with synthetic or authorized identities while preserving situational context.
▶️ How to Use
- Upload or paste the URL of the video to edit.
- Upload a face/head reference image for the identity you want to swap in.
- Select the output resolution (480p or 720p).
- (Optional) Set a seed if you need reproducible results.
- Click Run to generate the swapped video.
- Review the result; if needed, adjust the reference portrait or seed and run again.
📌 Tips & Notes
- Use sharp, well-lit videos where the face is not heavily occluded or motion-blurred.
- For the reference portrait, keep expression and angle reasonably close to the target shot for the cleanest match.
- Avoid extreme mismatches in lighting (e.g., dark blue stage light in video vs. warm daylight portrait) unless you want a stylized look.
- Ensure you have the legal right and consent to use all uploaded videos and portraits.
Authentication
For authentication details, please refer to the Authentication Guide.
API Endpoints
Submit Task & Query Result
set -euo pipefail
export WAVESPEED_API_KEY="your-api-key"
REQUEST_BODY=$(cat <<'JSON'
{
"video": "https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4",
"face_image": "https://interactive-examples.mdn.mozilla.net/media/cc0-images/painted-hand-298-332.jpg",
"resolution": "480p"
}
JSON
)
# 1. Submit the prediction.
SUBMIT_RESPONSE=$(curl --silent --show-error --fail-with-body \
-X POST "https://api.wavespeed.ai/api/v3/wavespeed-ai/video-head-swap" \
-H "Authorization: Bearer ${WAVESPEED_API_KEY}" \
-H "Content-Type: application/json" \
-d "${REQUEST_BODY}")
TASK=$(printf '%s' "${SUBMIT_RESPONSE}" | jq 'if type == "object" and has("data") then .data else . end')
PREDICTION_ID=$(printf '%s' "${TASK}" | jq -r '.id // empty')
if [ -z "${PREDICTION_ID}" ]; then
printf 'Submission response did not contain a prediction id
' >&2
exit 1
fi
RESULT_URL="https://api.wavespeed.ai/api/v3/predictions/${PREDICTION_ID}/result"
# 2. Poll until the prediction finishes.
while true; do
RESPONSE=$(curl --silent --show-error --fail-with-body \
"${RESULT_URL}" \
-H "Authorization: Bearer ${WAVESPEED_API_KEY}")
RESULT=$(printf '%s' "${RESPONSE}" | jq 'if type == "object" and has("data") then .data else . end')
STATUS=$(printf '%s' "${RESULT}" | jq -r '.status // empty')
case "${STATUS}" in
completed) printf '%s\n' "${RESULT}" | jq '.outputs'; break ;;
failed|cancelled|timeout|deleted) printf '%s\n' "${RESULT}" | jq . >&2; exit 1 ;;
*) sleep 2 ;;
esac
doneParameters
Task Submission Parameters
Request Parameters
| Parameter | Type | Required | Default | Range | Description |
|---|---|---|---|---|---|
| video | string | Yes | - | The video that contains the face to be replaced. | |
| face_image | string | Yes | - | - | The face image as reference. |
| prompt | string | No | - | The prompt to guide the model's behavior. | |
| resolution | string | No | 480p | 720p, 480p | The resolution of the output video. |
| seed | integer | No | - | - | The seed used for the prediction. |
Response Parameters
| Parameter | Type | Description |
|---|---|---|
| code | integer | HTTP status code (e.g., 200 for success) |
| message | string | Status message (e.g., “success”) |
| data.id | string | Unique identifier for the prediction, Task Id |
| data.model | string | Model ID used for the prediction |
| data.outputs | array | Output values, usually URL strings; some models return text strings or structured result objects (empty when status is not completed) |
| data.urls | object | Object containing related API endpoints |
| data.status | string | Task status. completed is successful; failed, cancelled, timeout, and deleted are failure terminal statuses. |
| data.created_at | string | ISO timestamp of when the request was created (e.g., “2023-04-01T12:34:56.789Z”) |
| data.error | string | Error message (empty if no error occurred) |
| data.timings | object | Object containing timing details |
| data.timings.inference | integer | Inference time in milliseconds |
Result Request Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| id | string | Yes | - | Task ID |
Result Response Parameters
| Parameter | Type | Description |
|---|---|---|
| code | integer | HTTP status code (e.g., 200 for success) |
| message | string | Status message (e.g., “success”) |
| data | object | The prediction data object containing all details |
| data.id | string | Unique identifier for the prediction |
| data.model | string | Model ID used for the prediction |
| data.outputs | array<string | object> | Array of generated outputs (empty when status is not completed). Items are usually URL strings, but may be text strings or structured result objects, depending on the model. |
| data.urls | object | Object containing related API endpoints |
| data.status | string | Status: completed is successful; failed, cancelled, timeout, and deleted are failure terminal statuses |
| data.created_at | string | ISO timestamp of when the request was created |
| data.error | string | Error message (empty if no error occurred) |
| data.timings | object | Object containing timing details |
| data.timings.inference | integer | Inference time in milliseconds |