Digital human / editing APIs

Avatar library, digital-human video, lip sync, motion transfer, and social-style editing — all on one sk-gpushare-* key

Register a reusable digital-human avatar from one photo or a short video, then drive it with audio or text to produce video. You can also lip-sync an existing video, transfer the motion from one video onto still portraits, or hand a talking-head video to social-style editing to have subtitle effects, a title, template styling and background music added automatically.

Authentication is identical to every other endpoint — the same sk-gpushare-* key in whichever of the four forms you prefer (x-api-key or x-goog-api-key header, ?key= query, Authorization: Bearer); see Authentication. Everything bills against your account balance (shared by all keys), and an insufficient balance returns 402 quota_exceeded.

EndpointPurposeBilling
POST /v1/videos/avatarsRegister an avatar (async)per call
GET /v1/videos/avatarsYour avatar libraryfree
GET /v1/videos/avatars/{id}Check an avatar's creation statusfree
DELETE /v1/videos/avatars/{id}Delete an avatarfree
GET /v1/videos/avatars/presetsThe platform's built-in avatarsfree
POST /v1/videos/generationsProduce video (digital human / lip sync / motion transfer per second; social-style editing per task)per second / per task
GET /v1/videos/generations/{id}Poll a production taskfree
GET /v1/videos/clip-templatesSocial-style editing templates (paginated)free
GET /v1/videos/clip-templates/categoriesSocial-style editing template categoriesfree
POST /v1/videos/clip-subtitlesSocial-style editing subtitle recognition (synchronous)per call (currently 0)

Producing and polling use the same generic video endpoints from the media APIs; only the model and the body fields change. Each variation below comes with a complete example.


1. The avatar library#

POST /v1/videos/avatars#

Register an avatar from a front-facing photo (or a video of the person). This is async: you get a pending record straight away and can only produce video once it polls ready, typically 5–10 minutes upstream.

{
  "name": "My presenter",
  "source_url": "https://example.com/portrait.jpg",
  "source_kind": "image"
}
FieldRequiredNotes
nameyes1–20 characters, the avatar's name in your library
source_urlyesA publicly reachable direct http(s) link. Upstream fetches the material itself and we offer no upload endpoint — host it yourself (object storage, a CDN, any public direct link)
source_kindnoimage (default) or video

Response:

{
  "id": "9f1c…",
  "name": "My presenter",
  "source_kind": "image",
  "status": "pending",
  "error": null,
  "created_at": "2026-07-29T08:12:00+00:00"
}

That id is the avatar reference you'll use from now on — put it in the avatar field when producing video; the underlying upstream identifier is not your concern.

GET /v1/videos/avatars/{id}#

Poll the creation status, every 15–30 seconds.

{ "id": "9f1c…", "name": "My presenter", "status": "ready", "error": null, "created_at": "…" }

status has three states: pending / ready / failed. Failures are refunded in full automatically — the refund uses this record as its evidence, which is why deletion is blocked while it is pending.

GET /v1/videos/avatars#

Lists this account's avatars (up to 200, newest first).

DELETE /v1/videos/avatars/{id}#

Deletes an avatar. One still being created (pending) can't be deleted — wait until it succeeds or fails.

GET /v1/videos/avatars/presets#

The platform's built-in avatars, free to use directly with no registration:

{ "avatars": [ { "id": "…", "name": "Professional female presenter" }, … ] }

Drop any id from presets straight into the avatar field of a production request.


2. Digital-human video (dh-avatar)#

Make a ready avatar speak. The driving audio or script sets the length, and you're billed per second of finished video.

Option 1: drive with audio#

{
  "model": "dh-avatar",
  "avatar": "9f1c…",
  "audio_url": "https://example.com/voice.mp3",
  "duration": 32
}

Option 2: drive with text plus a voice (one step, no separate synthesis)#

{
  "model": "dh-avatar",
  "avatar": "9f1c…",
  "voice": "<voice id>",
  "text": "Hello everyone — today I want to introduce…",
  "duration": 30
}

voice can be either:

  • the id of a voice you cloned through POST /v1/audio/voices, or
  • a platform preset voice id from the presets in GET /v1/audio/voices.

duration is required, in seconds. It's the basis for our cost hold — use the real length of the driving audio, or estimate from the script (Mandarin narration runs at roughly characters ÷ 3.3 seconds). When the task ends we settle on the actual seconds upstream reports and refund the excess automatically; the hold also caps what this production can cost.

Submit and poll on the generic video endpoints:

curl https://api.dflop.top/v1/videos/generations \
  -H "Authorization: Bearer $GPUSHARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"dh-avatar","avatar":"9f1c…","audio_url":"https://example.com/voice.mp3","duration":32}'
# → {"id":"…","status":"queued","model":"dh-avatar","created_at":1753…}

curl https://api.dflop.top/v1/videos/generations/<id> \
  -H "Authorization: Bearer $GPUSHARE_API_KEY"
# → {"id":"…","status":"succeeded","video_url":"https://…","expires_at":…}

3. Lip sync (dh-lipsync / -pro / -max)#

Swap the audio track on an existing video of a person and match the lips to it. The three tiers step up in quality and price.

{
  "model": "dh-lipsync-pro",
  "source_video_url": "https://example.com/source.mp4",
  "audio_url": "https://example.com/new-voice.mp3",
  "duration": 45
}

The finished length follows the driving audio. Both the source video and the audio must be publicly reachable direct links.


4. Motion transfer (dh-motion)#

Transfer the motion from a source video onto 1–7 portraits.

{
  "model": "dh-motion",
  "source_video_url": "https://example.com/dance.mp4",
  "face_count": 2,
  "resolution": "standard",
  "content": [
    { "type": "image_url", "image_url": { "url": "https://example.com/person1.jpg" } },
    { "type": "image_url", "image_url": { "url": "https://example.com/person2.jpg" } }
  ],
  "duration": 20
}
FieldNotes
source_video_urlThe motion source video (public direct link)
content[]1–7 portraits
face_countHow many people are in frame, 1–7; should match the number of portraits
resolutionfast / standard (default) / max — the tier sets the unit price; see the pricing table below
durationRequired; use the source video's length

The finished length follows the motion source video.


5. Social-style editing (clip-compose)#

Turn a video with clear speech into a finished, packaged piece: subtitle effects, keyword highlights, a title, a name card, a template style, background music and picture-in-picture inserts. It takes four steps: pick a template → recognise subtitles → submit the compose → poll for the result.

  • The key's model allowlist must permit clip-compose (the template, category and subtitle helper endpoints check the same thing); otherwise you get 403 model_not_allowed.
  • The source video must be ≤ 5 minutes with clearly audible speech, at a publicly reachable http(s) direct link (the platform offers no upload endpoint).
  • A finished video typically arrives in 20–60 seconds.

Step 1: pick a template#

# Template categories (six: 高级感 / 热门 / 简约 / 综艺感 / 本地引流 / 其他)
curl https://api.dflop.top/v1/videos/clip-templates/categories \
  -H "Authorization: Bearer $GPUSHARE_API_KEY"
# → {"list":[{"cate_id":"67d402f96574a9003050ea3a","cate_name":"高级感","sort":1}, …]}

# Templates (with cate_id: that category only; without: every template)
curl "https://api.dflop.top/v1/videos/clip-templates?cate_id=67d402f96574a9003050ea3a" \
  -H "Authorization: Bearer $GPUSHARE_API_KEY"
# → {"list":[{"style_id":"…","name":"…","cover_url":"https://…","demo_url":"https://…","cate_name":"","scene":""}, …],
#    "sid":"","api_version":"old"}

The template list is not paginated: one call returns the whole category (or every template), and sid is always an empty string. demo_url is the template's sample video. Put the chosen template's style_id into the compose request's video_style_id. Both endpoints are free.

Subtitle effects, keyword highlights, the title and the name card are all rendered by the template: compose without a template and none of them appear — only the picture-in-picture inserts are overlaid. Always send video_style_id. Only templates listed by this endpoint are accepted; a style_id from anywhere else is rejected (400 "这套模板已经下架了").

Step 2: recognise subtitles#

curl https://api.dflop.top/v1/videos/clip-subtitles \
  -H "Authorization: Bearer $GPUSHARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"video_url":"https://example.com/talk.mp4"}'

This is synchronous, usually 5–10 seconds; it waits at most 240 seconds and then returns 504 upstream_timeout. The response:

{
  "model": "clip-subtitle",
  "asr_id": "asr_…",
  "api_version": "old",
  "segments": [
    { "showRange": [0, 1750], "content": "很多人做事总纠结" },
    { "showRange": [1750, 2850], "content": "完美才开始" }
  ],
  "sentences": [
    { "showRange": [0, 1750], "content": "很多人做事总纠结" },
    { "showRange": [1750, 2850], "content": "完美才开始" }
  ]
}
FieldNotes
asr_idThe id of this recognition — required when composing
segmentsThe result by sentence: one sentence per item, showRange is [startMs, endMs], no punctuation
sentencesThe same as segments (kept for compatibility)

A video with no recognisable speech returns 400.

Step 3: submit the compose#

curl https://api.dflop.top/v1/videos/generations \
  -H "Authorization: Bearer $GPUSHARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "clip-compose",
    "video_url": "https://example.com/talk.mp4",
    "asr_id": "<asr_id from step 2>",
    "video_style_id": "<style_id from step 1>",
    "title": "Compound interest in three minutes",
    "enable_title_effect": true,
    "enable_caption_effect": true,
    "enable_keyword_effect": true,
    "name_card": { "name": "Ms Zhang", "description": "Financial planner" }
  }'
# → {"id":"…","status":"queued","model":"clip-compose","created_at":1790…}
FieldRequiredNotes
modelyesAlways clip-compose
video_urlyesThe source video's direct link — the same one you ran subtitles on in step 2
asr_idyesThe asr_id returned in step 2
enable_title_effectnoAdd a title effect; default false
enable_caption_effectnoAdd subtitle effects; default true
enable_keyword_effectnoHighlight keywords; default true
titlenoTitle text, at most 30 characters (longer is truncated)
video_style_idnoA template's style_id (step 1). Send it: without a template the video gets no subtitles, title or name card
music_urlnoA background music direct link (http(s))
name_cardnoA name card {"name": "…", "description": "…"}; name at most 20 characters, description at most 40
sentencesnoOnly if you edited the subtitles — rules below; omit it to use step 2's original recognition as is
pip_confignoPicture-in-picture inserts, at most 20: [{"media_url": "https://…", "begin_time": 3000, "end_time": 6000}], times in milliseconds, end_time > begin_time

sentences: edit the words, keep the lines#

Send sentences only when your user changed the subtitle text. The rule is one line for one line:

"sentences": [
  { "showRange": [0, 1750], "content": "好多人做事总纠结" },
  { "showRange": [1750, 2850], "content": "完美,才开始" }
]
  • Start from step 2's segments: same number of lines, same order, and change only content. Lines may get longer or shorter and may gain punctuation.
  • Don't split, merge or delete lines. A different line count returns 400 telling you how many lines are expected. (Upstream doesn't reject such subtitles — it silently drops all your edits or cuts the end of the video — so the platform stops the request before it is submitted, and nothing is charged.)
  • No line's content may be empty.
  • Timing comes from step 2's recognition; any showRange you send is ignored.
  • If nothing was edited, omit sentences entirely and the original recognition is used.
  • asr_id must be the one step 2 just returned; if an old recognition has expired you get 400 "字幕识别结果已经过期" — just run step 2 again.

Step 4: poll for the result#

Exactly like any other video task: poll GET /v1/videos/generations/{id}, every 5–10 seconds or so:

curl https://api.dflop.top/v1/videos/generations/<id> \
  -H "Authorization: Bearer $GPUSHARE_API_KEY"
# → {"id":"…","status":"succeeded","video_url":"https://…","expires_at":…}

Social-style editing pricing#

Endpoint / model IDPriceNotes
clip-compose (compose)48 per taskA flat price per successful compose task, regardless of video length; failures are refunded in full automatically
clip-subtitle (subtitle recognition)per call, currently 0Charged only on success; failures and timeouts cost nothing
Templates / categoriesfree

Pricing#

For the per-second SKUs, settlement uses the actual seconds upstream reports; duration sizes the hold on submit and the excess is refunded. clip-compose is a flat price per task and needs no duration.

Model IDPurposePrice
dh-avatar-createRegister an avatar220.11 each
dh-avatarDigital-human video3.96 / second
dh-lipsyncLip sync · standard4.04 / second
dh-lipsync-proLip sync · HD8.09 / second
dh-lipsync-maxLip sync · UHD12.13 / second
dh-motionMotion transferfast 4.04 / standard 8.09 / max 12.13 per second
clip-composeSocial-style editing48 per task (regardless of length)

The companion voice endpoints (synthesis and cloning) are in the media APIs.


Async and timeout conventions#

  • Avatar registration, video production and social-style compose are all async (social-style subtitle recognition is synchronous): submitting returns immediately and you poll the status endpoint afterwards. Polling itself is free.
  • A production task's video_url is a time-limited link, with expires_at in the response as its expiry — copy it somewhere of your own if you need it long-term.
  • Failed tasks (an upstream error, a timeout, expiry) are refunded in full automatically; you need do nothing.
  • The avatar record is the refund's evidence: calling DELETE while it's pending returns 400, so wait for a final state.

Common errors#

HTTPcodeMeaning
400invalid_request_errorThe avatar or voice reference doesn't exist, belongs to another account, or isn't ready yet; duration is missing; name is too long; source_url / video_url isn't http(s); subtitle recognition found no speech; pip_config start/end times are invalid
401authentication_errorThe key is invalid or disabled
402quota_exceededInsufficient account balance
403model_not_allowedThis key's model allowlist doesn't include that SKU
404model_not_foundThe model id is wrong, or that SKU isn't available yet
503no_channel_availableNo upstream is currently available for that SKU; retry later
504upstream_timeoutSubtitle recognition didn't finish within 240 seconds (not charged); retry, or use a shorter video

The error body matches every other endpoint — see Error codes.