Promoat.Partner API

Videos

Talking-head videos

One vertical shot of your character presenting to camera — the workhorse format for updates, promos, and UGC-style ads. Up to 59.5 seconds, lipsync always on, ready to post.

How a job runs

POST /videos with mode: "talking_head" queues an async job and returns immediately. The job makes one 9:16 first frame of your character (from your video_idea, or uses the frame you supply), gets the speech (synthesized from your script, or your recording), lip-syncs it in windows of 5–14.5 s cut at natural pauses, and merges them into one vertical video. Captions are burned in if you asked for them. You poll the job until it hands you a video_url.

The request

POST/api/partner/v1/videos
FieldTypeDescription
mode"talking_head" requiredOr "scenes" — see Scene videos.
character_iduuid requiredWho is on camera.
voice_id + scriptstringsAudio source A — the job speaks script (≤4000 characters) in your voice, verbatim.
audio_urlhttps urlAudio source B — your recording (or a POST /tts result). Provide exactly one source. Duration is measured server-side. A non-https URL is ignored with a FIELD_IGNORED warning.
video_ideastring ≤2000The scene to stage. Required unless you supply a frame (first_frame_url or first_frame_asset_id).
first_frame_urlhttps urlAnimate exactly this still. Non-https URLs are ignored with a FIELD_IGNORED warning.
first_frame_asset_iduuidA first_frame asset you made with POST /images/first-frame (kind: "talking_head", the default). Not together with first_frame_url. A scenes frame here → 400 FIRST_FRAME_KIND_MISMATCH.
resolutionstring480P | 768P (default) | 1080P; lowercase and 480p / 720p / 1080p are accepted. 720p is served as 768P with a RESOLUTION_MAPPED warning.
voice_settingsobjectScript mode: speed (0.7–1.2), stability, similarity_boost, style (0–1). Out-of-range values are clamped.
languagestringSpoken language — ISO code or name ("ar", "spanish").
use_dynamic_captionsbooleanBurn in dynamic captions — an add-on, 10 credits. See below.
captions_languagestringOptional caption language, e.g. "en", "ar". Omit to auto-detect.
captions_position"top" | "center" | "bottom"Where the caption band sits. Default "bottom".
topic / audiencestringsAccepted but no effect (FIELD_IGNORED); put the brief in video_idea / scene_prompt.
expected_creditsint ≥0The price you quoted. If the server quote is higher → 409 QUOTE_MISMATCH, nothing charged.
client_request_iduuidIdempotency key (or send the Idempotency-Key header). If both are sent they must match.
Legacy fields are accepted and ignored

fast, lipsync, narration_mode, audio_duration, first_frame_titleand other legacy tuning fields no longer do anything. They don't fail the request; each comes back as a FIELD_IGNORED warning.

The response

200 OK
{
  "workflow_id": "partner_h3t_8f2c…",
  "status": "queued",
  "credits_reserved": 125,
  "credits_quoted": 125,
  "job_type": "talking_head_video",
  "resolution": "768P",
  "price_table_version": "2026-09-30",
  "warnings": [
    { "field": "fast", "code": "FIELD_IGNORED", "message": "…" }
  ],
  "duplicate": false
}

Retried with the same idempotency key and body? You get { workflow_id, status, duplicate: true, credits_reserved } for the original job and nothing is charged twice.

Dynamic captions

Captions remain an add-on. Set use_dynamic_captions: true for big, bold captions that reveal word by word, on a solid band, in Latin and Arabic scripts (pass captions_language: "ar" for Arabic). Flat 10 credits, quoted as a captions line. Choose captions_position top, center or bottom to keep them off your subject; when you build the opening still, leave that strip clear.

Limits

Audio: 1 s to 59.5 s

Under 1 s → 400 AUDIO_TOO_SHORT; 1–5 s is billed as 5 s. Over 59.5 s (a recording measured, or a script estimated) → 400 AUDIO_TOO_LONG at queue time, nothing charged. Scripts are capped at 4,000 characters. Rule of thumb: keep scripts under about 120 words.

What it costs

Generation is billed per second of output at the resolution's rate (480P 5/s, 768P 8/s, 1080P 16/s), plus a first frame when the job makes one, a small transcription line, the speech in script mode, and captions if chosen. Examples at the default 768P:

Line15 s, your audio…with your own frame…with captions
generation120120120
first_frame4—4
transcription111
captions——10
Total125121135

The smallest video — 5 s at 480P — is 30 credits. With voice_id + script the quote adds a tts line and is based on an estimated length with headroom; you are charged the measured price if it is lower (how billing settles).

Quote it, then queue it

1. Estimate (free, no side effects)
curl -X POST https://platform.promoat.ai/api/partner/v1/videos/estimate \
  -H "Authorization: Bearer $PROMOAT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "talking_head",
    "character_id": "CHARACTER_ID",
    "audio_url": "https://your-cdn.com/audio/15-second-take.mp3",
    "video_idea": "standing in a bright modern kitchen, smiling at the camera",
    "resolution": "768P"
  }'

# → { "mode": "talking_head", "credits_required": 125, "credits_balance": 961,
#     "sufficient_credits": true, "estimated_seconds": 15, "billed_seconds": 15,
#     "resolution": "768P", "job_type": "talking_head_video",
#     "price_table_version": "2026-09-30",
#     "lines": [
#       { "kind": "generation", "credits": 120, "quantity": 15, "unit": "output_second" },
#       { "kind": "first_frame", "credits": 4 },
#       { "kind": "transcription", "credits": 1 }
#     ],
#     "breakdown": { "render_credits": 121, "lipsync_credits": 0, "tts_credits": 0,
#                    "first_frame_credits": 4, "captions_credits": 0 },
#     "warnings": [] }
2. Queue (same body + the quote + an idempotency key)
curl -X POST https://platform.promoat.ai/api/partner/v1/videos \
  -H "Authorization: Bearer $PROMOAT_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4812-video-1" \
  -d '{ ...same body..., "expected_credits": 125 }'

# → { "workflow_id": "partner_h3t_…", "status": "queued", "credits_reserved": 125, … }
3. Poll (every 8–10 s)
curl https://platform.promoat.ai/api/partner/v1/jobs/partner_h3t_…/status \
  -H "Authorization: Bearer $PROMOAT_KEY"

# → { "status": "running", "phase": "generating", "progress": 40, … }
# → { "status": "completed", "video_url": "https://…/final.mp4",
#     "poster_url": "https://…/poster.jpg", "credits_consumed": 125, … }

Up to 5 jobs can run at once (429 beyond that). Finished videos can be changed scene by scene — see Video edits.

Script mode with a ready-made frame

voice_id + script + first_frame_asset_id
curl -X POST https://platform.promoat.ai/api/partner/v1/videos \
  -H "Authorization: Bearer $PROMOAT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "talking_head",
    "character_id": "CHARACTER_ID",
    "voice_id": "VOICE_ID",
    "script": "Hi! Three quick updates from the shop this week…",
    "language": "en",
    "voice_settings": { "speed": 1.05, "stability": 0.5 },
    "first_frame_asset_id": "FRAME_ASSET_ID",
    "resolution": "1080P",
    "use_dynamic_captions": true,
    "captions_position": "center",
    "client_request_id": "6f1d3c1e-8a55-4c1f-9d55-2f0f8f0d2b11"
  }'

Try it

Try itGET/api/partner/v1/charactersfree
Add your API key in the sidebar to enable this.
Try itPOST/api/partner/v1/videos/estimatefree
Add your API key in the sidebar to enable this.
Try itPOST/api/partner/v1/videoscharges the quoted amount (real video job)
Add your API key in the sidebar to enable this.
Try itGET/api/partner/v1/jobs/{workflow_id}/statusfree
Add your API key in the sidebar to enable this.