Start here
What you get
This API puts FazeSwap’s face and video models behind an HTTP call. You post an image, a clip or a live stream; you get back the same thing with a different face on it. Seven features, one key, nothing to install.
Two shapes of call. Most features are asynchronous: you submit a job, poll it, and collect a signed URL when it finishes. Full Live Swap is a session — you open it, stream through it, and close it, charged by the second you actually use.
Every call is authenticated with a bearer key and paid for from a balance you top up in advance. Nothing recurring, no floor to clear, and a failed job costs nothing.
Start here
Your first call
Three steps to your first render: upload an asset, submit a job, poll for the result.
# 1 · Upload an asset → get a file_key
FILE_KEY=$(curl -s https://api.fazeswap.com/api/v1/uploads \
-H "Authorization: Bearer $FAZESWAP_API_KEY" \
-F "file=@base.png" | jq -r .file_key)
# 2 · Submit the job
JOB=$(curl -s https://api.fazeswap.com/api/v1/face-swap-image \
-H "Authorization: Bearer $FAZESWAP_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"image_key\":\"$FILE_KEY\",\"face_key\":\"$FACE_KEY\"}")
# 3 · Poll until it is terminal
curl -s https://api.fazeswap.com/api/v1/jobs/$(echo "$JOB" | jq -r .id) \
-H "Authorization: Bearer $FAZESWAP_API_KEY"Start here
Using your key
Send your key as a bearer token on every request.
Authorization: Bearer fzs_live_xxxxxxxxxxxxxxxxxxxxxxxxKeep your key secret
Start here
Endpoints & versions
https://api.fazeswap.com/api/v1How it works
Sending files
Capability calls reference inputs by file_key, not by URL. Upload each asset first and pass back the key you get. We host the upload — there is no CORS or presign dance, and no external URLs are fetched.
/v1/uploadsmultipart/form-data| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | Image, audio or video. Type is detected from the content type. |
{
"file_key": "api/images/9f8c…/base.png",
"url": "https://…"
}Images and audio up to 45 MB, video up to 500 MB.
How it works
Getting results back
Every capability except Full Live Swap is asynchronous: a submit returns immediately with an id, and you poll its status URL until it reaches a terminal state.
/v1/jobs/{job_id}{
"id": "…",
"status": "queued | processing | succeeded | failed",
"output_url": "https://…",
"charged_usd": 0.06,
"error": { "code": "face_not_detected", "message": "…" }
}Output URLs expire
You are billed when a job completes. A job that fails is refunded in full — you are never charged for a result you did not get.
How it works
Safe retries
Send an Idempotency-Key header on any submit. A retry carrying the same key returns the original job — no second charge, no duplicate render. A network timeout costs you nothing.
-H "Idempotency-Key: your-own-unique-id"How it works
Throughput caps
60 requests per minute by default. Exceeding it returns 429 with code rate_limited.
rate_limited is not at_capacity
rate_limited means you are sending too fast — back off and retry. at_capacity means the platform is briefly saturated; retry shortly. They look alike and need different responses.How it works
When a call fails
Errors carry a stable code you can branch on and a message meant for a human. Branch on the code — the prose may change, the codes will not.
| HTTP | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing, unknown or revoked key. |
| 402 | insufficient_balance | Top up to continue. |
| 400 | invalid_input | A field failed validation. |
| 400 | unsupported_format | That file type isn't accepted. |
| 400 | input_too_large | Over the size or duration limit. |
| 400 | capability_unavailable | Currently switched off. |
| 404 | not_found | No such job, or not yours. |
| 429 | rate_limited | Slow down and retry. |
| 429 | at_capacity | Briefly saturated — retry shortly. |
| 503 | capacity_unavailable | No capacity for a live session right now. |
| 503 | capacity_warming | Voice capacity is starting up — retry in about a minute. |
| 500 | internal_error | Failed on our side. Nothing was billed; retry, and tell us if it persists. |
| — | face_not_detected | Job-level: no face in the input. |
| — | content_rejected | Job-level: failed a content check. |
| — | processing_failed | Job-level: the render didn't complete. |
Features
Character Swap
Put your character into a reference video, keeping its motion.
/v1/character-swap| Field | Type | Required | Description |
|---|---|---|---|
video_key | string | Yes | file_key of the reference video. |
character_key | string | Yes | file_key of the character image. |
resolution | string | No | 1k or 2k. Defaults to 1k. |
callback_url | string | No | Webhook POSTed the terminal status. |
Features
Face Swap — video
Swap a face into a video.
/v1/face-swap| Field | Type | Required | Description |
|---|---|---|---|
video_key | string | Yes | file_key of the source video. |
face_key | string | Yes | file_key of the face to swap in. |
output_resolution_p | int | No | 480, 720 or 1080. |
Features
Face Swap — image
Swap a face into a single image.
/v1/face-swap-image| Field | Type | Required | Description |
|---|---|---|---|
image_key | string | Yes | file_key of the base image. |
face_key | string | Yes | file_key of the face to swap in. |
curl https://api.fazeswap.com/api/v1/face-swap-image \
-H "Authorization: Bearer $FAZESWAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"image_key":"api/images/…","face_key":"api/images/…"}'Features
Motion Control
Animate a still character image to match a reference video’s motion.
/v1/motion-control| Field | Type | Required | Description |
|---|---|---|---|
image_key | string | Yes | file_key of the character image. |
motion_video_key | string | Yes | file_key of the motion reference. |
Features
Avatar
A talking avatar from one portrait and a script.
/v1/avatar| Field | Type | Required | Description |
|---|---|---|---|
image_key | string | Yes | file_key of the source portrait. |
script | string | Yes | What the avatar says. Up to 5,000 characters. |
voice_id | string | No | A specific voice. A default is chosen if omitted. |
language | string | No | Language hint for the voice. |
output_resolution_p | int | No | 480, 720 or 1080. |
Features
Lip Sync
Match a video’s mouth movement to your audio.
/v1/lip-sync| Field | Type | Required | Description |
|---|---|---|---|
video_key | string | Yes | file_key of the source video. |
audio_key | string | Yes | file_key of the audio to sync to. |
output_resolution_p | int | No | 480, 720 or 1080. |
Features
Full Live Swap
Real-time face swap over a live stream. Unlike the others this is a session, not a job: create one, connect over WebSocket, and stream.
/v1/full-live-swap/session| Field | Type | Required | Description |
|---|---|---|---|
duration_minutes | int | Yes | Minutes to fund. The stream hard-stops here. 1–240. |
prompt | string | No | Optional swap instruction. |
{
"session_id": "…",
"stream_url": "wss://api.fazeswap.com/realtime",
"session_token": "rt_…",
"max_duration_sec": 600,
"max_cost_usd": 25.00
}{stream_url}?session_token={session_token}You pay for seconds streamed, not the block reserved
max_cost_usd is the ceiling — keep at least that much available to start. You are billed for the seconds you actually stream, and a session that never starts costs nothing.Features
Voice Changer
Real-time voice conversion. Like Full Live Swap this is a session rather than a job: open one, stream audio frames over the socket, and receive converted audio back on the same connection.
Pick a target voice first. The reference clip stays on our side — you never handle it.
/v1/voices[
{
"id": "8f2c…",
"name": "Narrator",
"description": "Warm, measured.",
"preview_url": "https://…/preview.mp3"
}
]/v1/voice/session| Field | Type | Required | Description |
|---|---|---|---|
duration_minutes | int | No | Minutes to fund. The session hard-stops here. 1–120, default 10. |
voice_profile_id | string | No | id from GET /v1/voices. Omit to pass audio through unchanged — useful for measuring latency before picking a voice. |
{
"session_id": "…",
"stream_url": "wss://api.fazeswap.com/realtime",
"session_token": "rt_…",
"max_duration_sec": 600,
"max_cost_usd": 3.60
}{stream_url}?session_token={session_token}On connect we hand the engine your chosen voice, then send you one JSON frame with the audio format to use in both directions:
{ "sample_rate": 22050, "chunk_frames": 4096 }After that it is audio both ways: send raw mono PCM (int16, little-endian, at the sample_rate above) as binary frames, and converted audio comes back in the same format. Send roughly chunk_frames per message — much smaller wastes round-trips, much larger adds latency.
You send audio, nothing else
Billed by the second, like Full Live Swap
There is no on-device fallback
capacity_unavailable or capacity_warming rather than returning a session that cannot carry audio. Retry shortly — warming is usually under a minute.Billing & usage
Your balance
Check your prepaid balance and spend from your own system.
/v1/balance{
"balance_usd": 84.20,
"total_spent_usd": 15.80,
"spent_this_week_usd": 4.10,
"spent_this_month_usd": 15.80,
"spent_this_year_usd": 15.80
}Billing & usage
What you’ve spent
/v1/usage?limit&from&to/v1/usage/summary?from&toUse /usage/summary for totals — it is computed from your full history. The /usage log is capped at 500 rows, so summing it under-reports once you are busy.
Billing & usage
Rate card
Live rates, read from the API itself — this table cannot go stale. Public, no authentication required.
/v1/pricingno auth