Openjutsu Character Swap API Documentation

Openjutsu Character Swap API Documentation

Playground

Try it on WaveSpeedAI!

Openjutsu Character Swap replaces people in existing videos with characters from reference images while preserving the original scene, motion, framing, and audio for character replacement, creative video editing, social content, and production workflows. Ready-to-use REST inference API, best performance, no coldstarts, affordable pricing.

Features

Genjutsu Character Swap (OpenJutsu) replaces people in an existing video with characters from reference images while preserving the original scene, camera movement, performance, and audio.

Use one image to replace the most prominent person, or provide multiple images with role descriptions to control which people should be replaced.


Why Choose This?

  • Character replacement — Replace people in an existing video with characters from reference images.
  • Up to 4 character images — Replace multiple people in the same scene.
  • Role-based matching — Describe who each reference image should replace.
  • Performance preserved — Keep the source video’s body motion, timing, framing, and camera movement.
  • Original audio preserved — Retain the source soundtrack in the final result.
  • Up to 1080p output — Choose 480p, 540p, 768p, or 1080p.

Parameters

ParameterRequiredDescription
videoYesSource video URL. Must be at least 2 seconds long and contain a visible person. Up to the first 15 seconds are processed.
imagesYesBetween 1 and 4 character image URLs, one for each person you want to replace.
rolesNoOptional descriptions identifying who each image should replace, in the same order as images. Example: the woman in black on the left.
resolutionNoOutput resolution: 480p, 540p, 768p, or 1080p. Default: 768p.
seedNoRandom seed for generation. A negative value uses a random seed.

How to Use

  1. Upload a source video — Use a continuous shot with at least one clearly visible person.
  2. Add character images — Provide one image for each person you want to replace.
  3. Add roles optional — Describe which person each image should replace when multiple people appear.
  4. Choose resolution — Select 480p, 540p, 768p, or 1080p.
  5. Set a seed optional — Use a fixed seed when comparing variations.
  6. Submit — Generate and retrieve the character-swapped video.

Pricing

Pricing is based on the input video duration, selected resolution, and number of character images.

ResolutionPrice per Second
480p$0.15
540p$0.17
768p$0.20
1080p$0.40

Each character image adds $0.02.

Example Costs

ConfigurationPrice
10s at 480p + 1 image$1.52
10s at 540p + 1 image$1.72
10s at 768p + 1 image$2.02
10s at 1080p + 1 image$4.02
10s at 768p + 2 images$2.04
15s at 768p + 1 image$3.02

Videos shorter than 3 seconds are billed as 3 seconds. Videos longer than 15 seconds use and bill only the first 15 seconds.


Best Use Cases

  • Character replacement — Replace actors or people while preserving the original performance.
  • Creative remixes — Recast existing footage with new characters.
  • Marketing content — Adapt existing video performances with different character appearances.
  • Storytelling — Reuse motion and cinematography while changing who appears in the scene.
  • Multi-character scenes — Replace selected people while leaving others unchanged.

Pro Tips

  • Use a continuous source shot with clearly visible people.
  • Use one reference image per person you want to replace.
  • Describe people by appearance or position rather than by name.
  • Use roles when several people appear in the same scene.
  • Use clear character images with visible facial and clothing details.
  • Avoid source footage where mirrors or reflections are important.

Notes

  • Input video duration is rounded up to the next whole second for billing.
  • The person to replace must be visible in the first frame.
  • Each roles entry supports up to 200 characters. The number of entries must not exceed the number of input images.
  • Up to 4 character images are supported.
  • With one image and no roles, the most prominent person is replaced.
  • With multiple images and no roles, images are matched to people by appearance order or left-to-right position.
  • Source audio is preserved.
  • Videos longer than 15 seconds use only the first 15 seconds.

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",
  "images": [
    "https://interactive-examples.mdn.mozilla.net/media/cc0-images/painted-hand-298-332.jpg"
  ],
  "resolution": "768p"
}
JSON
)

# 1. Submit the prediction once. Never retry this request automatically:
# a retry after an unclear failure can create a second, billed task.
CHECK='The task may already exist. Check History or the predictions list before submitting again.'
if ! SUBMIT_RESPONSE=$(curl --silent --show-error --fail-with-body \
  --connect-timeout 10 --max-time 60 \
  -X POST "https://api.wavespeed.ai/api/v3/wavespeed-ai/openjutsu/character-swap" \
  -H "Authorization: Bearer ${WAVESPEED_API_KEY}" \
  -H "Content-Type: application/json" \
  -d "${REQUEST_BODY}"); then
  printf 'Submit failed. %s\n' "${CHECK}" >&2
  exit 1
fi

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. %s\n' "${CHECK}" >&2
  exit 1
fi
RESULT_URL="https://api.wavespeed.ai/api/v3/predictions/${PREDICTION_ID}/result"

# 2. Poll until the prediction reaches a final status.
BODY_FILE=$(mktemp)
trap 'rm -f "${BODY_FILE}"' EXIT
while true; do
  # Transient errors (429, 5xx) are retried, honoring Retry-After.
  HTTP_CODE=$(curl --silent --show-error --fail-with-body \
    --connect-timeout 10 --max-time 60 --retry 5 --retry-delay 2 \
    --output "${BODY_FILE}" --write-out '%{http_code}' \
    "${RESULT_URL}" \
    -H "Authorization: Bearer ${WAVESPEED_API_KEY}" || true)
  case "${HTTP_CODE}" in
    2??) ;;
    000|429|5??)  # dropped connection or temporary error: keep polling
      printf 'Task %s: HTTP %s, polling again in 10s\n' "${PREDICTION_ID}" "${HTTP_CODE}" >&2
      sleep 10; continue ;;
    *) printf 'Task %s: HTTP %s\n' "${PREDICTION_ID}" "${HTTP_CODE}" >&2
      cat "${BODY_FILE}" >&2; exit 1 ;;
  esac
  RESULT=$(jq 'if type == "object" and has("data") then .data else . end' "${BODY_FILE}")
  STATUS=$(printf '%s' "${RESULT}" | jq -r '.status // empty')

  case "${STATUS}" in
    completed) printf '%s\n' "${RESULT}" | jq '.outputs'; break ;;
    failed|cancelled|timeout|deleted)
      printf 'Task %s ended with status %s\n' "${PREDICTION_ID}" "${STATUS}" >&2
      printf '%s\n' "${RESULT}" | jq . >&2; exit 1 ;;
    *) sleep 2 ;;
  esac
done

Retry temporary errors only while polling, never the submit request. Errors, retries and timeouts

Parameters

Task Submission Parameters

Request Parameters

ParameterTypeRequiredDefaultRangeDescription
videostringYes-URL of the source video. The people in it are replaced by your characters; the scene, motion and original audio are kept. Use a single shot with a person in the first frame. The output follows the video's length, up to 15 seconds.
imagesarray<string>Yes-1 ~ 4 itemsURLs of the character pictures, one per person to replace (up to 4). With one picture and no roles, the most prominent person is replaced. With several, or with roles, only the people you describe are replaced and everyone else stays as filmed. A picture nobody in the video matches is left out.
rolesarray<string>No-0 ~ 4 itemsOptional. One short description per picture, in the same order, saying who it replaces, for example "the woman in black on the left". Leave it out and the pictures go to the people in the order they appear in the video, or from left to right when they are on screen together.
resolutionstringNo768p480p, 540p, 768p, 1080pOutput resolution. The price per second rises with it.
seedintegerNo--The random seed to use for the generation. A negative value means a random seed will be used.

Every model also accepts the global request options enable_sync_mode and enable_base64_output; see their pages for details.

Response Parameters

ParameterTypeDescription
codeintegerHTTP status code (e.g., 200 for success)
messagestringStatus message (e.g., “success”)
data.idstringUnique identifier for the prediction, Task Id
data.modelstringModel ID used for the prediction
data.outputsarrayOutput values, usually URL strings; some models return text strings or structured result objects (empty when status is not completed)
data.urlsobjectObject containing related API endpoints
data.statusstringTask status. completed is successful; failed, cancelled, timeout, and deleted are failure terminal statuses.
data.created_atstringISO timestamp of when the request was created (e.g., “2023-04-01T12:34:56.789Z”)
data.errorstringError message (empty if no error occurred)
data.timingsobjectObject containing timing details
data.timings.inferenceintegerInference time in milliseconds

Result Request Parameters

ParameterTypeRequiredDefaultDescription
idstringYes-Task ID

Result Response Parameters

ParameterTypeDescription
codeintegerHTTP status code (e.g., 200 for success)
messagestringStatus message (e.g., “success”)
dataobjectThe prediction data object containing all details
data.idstringUnique identifier for the prediction
data.modelstringModel ID used for the prediction
data.outputsarray<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.urlsobjectObject containing related API endpoints
data.statusstringStatus: completed is successful; failed, cancelled, timeout, and deleted are failure terminal statuses
data.created_atstringISO timestamp of when the request was created
data.errorstringError message (empty if no error occurred)
data.timingsobjectObject containing timing details
data.timings.inferenceintegerInference time in milliseconds
© 2026 WaveSpeedAI. All rights reserved.