GoodTake AI Docs
Character Swap API

Character Swap Overview

Replace the person in a video with your character, keeping camera, motion, timing and audio

Overview

Character Swap puts a character created with the Character API into a customer-supplied video. You send an Instagram, TikTok, Threads or YouTube Shorts link — or a direct video URL — plus a character_id, and the person in the video is replaced by your character while the camera, motion, timing and (by default) the original audio are preserved.

Base URL: https://api.goodtake.ai/v1

No prompt, no model, no reference config in the request. The swap is compiled server-side and dispatched through the same generation pipeline as everything else. The model is pinned to Dreamina Seedance 2.5 (dreamina-seedance-2-5-260628).

Endpoints

MethodPathPurpose
POST/character-swapCreate a swap from a video link + character. Returns 202.
GET/character-swap/{id}Poll: derived status, source metadata, finished video.
GET/character-swapList your organization's swaps (paginated).
POST/character-swap/{id}/regenerateNew run from the stored source. Returns 202.
GET/character-swap/estimateQuote a swap before creating one.

Requirements

  • The character must have a completed sheet — Character API status ready, or at least a finished sheet image. Otherwise the create returns 422.
  • The source video must be 4–30 seconds. This is a Seedance 2.5 video-edit constraint. It is checked twice: an early advisory reject from the scraper's metadata, and an authoritative probe during ingest.

Supported sources

Social hosts are classified by hostname, not by path, and resolved through a per-platform resolver. Direct video URLs skip that step entirely.

Hostsource.kind
instagram.cominstagram
tiktok.com, vm.tiktok.com, vt.tiktok.comtiktok
threads.net, threads.comthreads
youtube.com, youtu.beyoutube
any other http(s) video URLdirect

Because classification is host-based, a link to a non-video path on a supported host still gets sent to that platform's resolver and comes back as a precise ingest error rather than an instant rejection.

Cost

One swap = one Seedance 2.5 video generation, charged on completion. Cost is token-based and scales with source duration and resolution — a swap bills tokens for the source clip and the generated output, so cost is roughly 2 × duration × the per-second token rate.

At current settings:

Resolution10s sourceApprox.
480p~87 credits~$2.61
720p~186 credits~$5.58
1080p~456 credits~$13.68

Quote before you create

GET /v1/character-swap/estimate?resolution=1080p&duration_seconds=10
curl -s "https://api.goodtake.ai/v1/character-swap/estimate?resolution=720p&duration_seconds=12" \
  -H "Authorization: Bearer $GOODTAKE_API_KEY" | jq .
{
  "estimated_cost": 223,
  "resolution": "720p",
  "duration_seconds": 12,
  "model": "dreamina-seedance-2-5-260628"
}

Durations under the provider's 4-second billing minimum are priced as 4s. Durations over 30s return 422.

Where the quote shows up

Momentestimated_cost
Create 202null — the source has not been ingested yet, so its duration (the main cost driver) is unknown. Carries estimated_cost_range {min, max} instead: the 4s and 30s brackets at the chosen resolution.
PollExact, once ingest has measured the source. Sits next to cost_credits (the actual charge, 0 until completion).
Regenerate 202Exact — the stored source's duration is already known.

The charge is capped at the quote. The billing dimensions carry the source duration as the requested output length, so a provider token overrun costs us margin, never you. Resolver runs and the audio processing are not billed separately.

Relationship to the Image API

Swap generations are visible in GET /image/generations — billed rows stay visible — but with an empty prompt, which is the only thing keeping the compiled prompt off the wire.

Explore