Skip to main content
Use one permitted source face across a list of target photos while keeping a durable record of every request. This workflow fits opt-in event photos, personalized creative, and photo-editing products. Magic Hour processes one photo project per API request; there is no bulk face-swap endpoint. The runners below call the companion single-photo script sequentially, wait for each result, and stop on the first error. Start with one image before running a batch.

Face Swap API page

Explore Face Swap API capabilities and pricing

Face Swap Photo reference

Check request fields and response schemas

Before you start

  1. Create an API key and set MAGIC_HOUR_API_KEY.
  2. Download the matching companion worker from this guide’s versioned source: example.py, example.mjs, or example.sh. These workers read SOURCE_FACE_URL and TARGET_IMAGE_URL, print PROJECT_ID before polling, and support PROJECT_ID recovery.
  3. Run that example with one source and one target. Confirm the output and credit charge before continuing.
  4. Create targets.txt beside the script with one direct HTTPS target-image URL per line. Use 1-100 images for the first batch.
  5. Set SOURCE_FACE_URL to a clear source image you control. Only process images and likenesses you have permission to use.
Every fresh create request uses generation credits. A timed-out client can hide an accepted job; preserve its log and reconcile the project before retrying.

Source, target, and cost

source_file_path supplies the face identity. target_file_path supplies the scene and faces to replace. With face_swap_mode: "all-faces", every detected target face receives the same identity. For different identities in a group photo, use face detection and individual face mappings instead. Photo face swap costs 10 credits per image, so 10 completed photos cost 100 credits and 100 cost 1,000 credits. A completed image that you reject creatively still uses credits. Check current billing options before scaling.

Run a bounded batch

Save the matching runner below as batch.py, batch.mjs, or batch.sh, then run it from the same folder as the single-photo example and targets.txt.

Expected result

Each batch-logs/000.log file contains the project ID, final charge, and expiring download URL, or the error that needs review. A matching .complete marker appears only after the single-photo script exits successfully. Completed items are skipped on rerun. A log without a completion marker stops the batch so an uncertain submission is not silently repeated. Download each completed output before its URL expires. Keep failed, timed-out, and completed work in distinct states.

Recover an interrupted item

If a log contains a project ID, set PROJECT_ID to that ID and rerun the single-photo script to resume polling without another create request. Append its output to the same log. After it exits successfully and you verify that the result belongs to the target, create the matching marker without overwriting an existing one:
Replace 001 with the reconciled log index. The next batch run skips that item and continues. If the log has no project ID after a timeout, do not submit the request again. Email support@magichour.ai with the POST /v1/face-swap-photo endpoint, request name, and UTC timestamp so the request can be traced first. Preserve the original logs; deleting them and rerunning the whole list can create duplicate charges. For a larger workload, replace these local runners with a durable job queue, bounded concurrency, webhook signature verification, and explicit per-item state in your database.

Troubleshooting

This guide handles photos. Video face swap has duration-based costs and longer processing times. For motion from a product photo, use the product-video recipe.