Create Talking Avatar
curl --request POST \
--url https://api.hooked.so/v1/project/create/talking-avatar \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data @- <<EOF
{
"script": "Check out this amazing product! It’s super easy to use and I love the results.",
"avatarId": "2",
"voiceId": "confident_voice_id'",
"lipsyncModel": "base",
"caption": {
"preset": "wrap1",
"alignment": "bottom",
"disabled": false
},
"audio": {
"speed": 1,
"stability": 0.5,
"similarityBoost": 0.75,
"style": 0,
"useSpeakerBoost": true
},
"webhook": "https://yoursite.com/webhook",
"metadata": {
"campaignId": "campaign_id",
"variant": "A"
}
}
EOFimport requests
url = "https://api.hooked.so/v1/project/create/talking-avatar"
payload = {
"script": "Check out this amazing product! It’s super easy to use and I love the results.",
"avatarId": "2",
"voiceId": "confident_voice_id'",
"lipsyncModel": "base",
"caption": {
"preset": "wrap1",
"alignment": "bottom",
"disabled": False
},
"audio": {
"speed": 1,
"stability": 0.5,
"similarityBoost": 0.75,
"style": 0,
"useSpeakerBoost": True
},
"webhook": "https://yoursite.com/webhook",
"metadata": {
"campaignId": "campaign_id",
"variant": "A"
}
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
script: 'Check out this amazing product! It’s super easy to use and I love the results.',
avatarId: '2',
voiceId: 'confident_voice_id\'',
lipsyncModel: 'base',
caption: {preset: 'wrap1', alignment: 'bottom', disabled: false},
audio: {
speed: 1,
stability: 0.5,
similarityBoost: 0.75,
style: 0,
useSpeakerBoost: true
},
webhook: 'https://yoursite.com/webhook',
metadata: {campaignId: 'campaign_id', variant: 'A'}
})
};
fetch('https://api.hooked.so/v1/project/create/talking-avatar', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hooked.so/v1/project/create/talking-avatar",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'script' => 'Check out this amazing product! It’s super easy to use and I love the results.',
'avatarId' => '2',
'voiceId' => 'confident_voice_id\'',
'lipsyncModel' => 'base',
'caption' => [
'preset' => 'wrap1',
'alignment' => 'bottom',
'disabled' => false
],
'audio' => [
'speed' => 1,
'stability' => 0.5,
'similarityBoost' => 0.75,
'style' => 0,
'useSpeakerBoost' => true
],
'webhook' => 'https://yoursite.com/webhook',
'metadata' => [
'campaignId' => 'campaign_id',
'variant' => 'A'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hooked.so/v1/project/create/talking-avatar"
payload := strings.NewReader("{\n \"script\": \"Check out this amazing product! It’s super easy to use and I love the results.\",\n \"avatarId\": \"2\",\n \"voiceId\": \"confident_voice_id'\",\n \"lipsyncModel\": \"base\",\n \"caption\": {\n \"preset\": \"wrap1\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"audio\": {\n \"speed\": 1,\n \"stability\": 0.5,\n \"similarityBoost\": 0.75,\n \"style\": 0,\n \"useSpeakerBoost\": true\n },\n \"webhook\": \"https://yoursite.com/webhook\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hooked.so/v1/project/create/talking-avatar")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"script\": \"Check out this amazing product! It’s super easy to use and I love the results.\",\n \"avatarId\": \"2\",\n \"voiceId\": \"confident_voice_id'\",\n \"lipsyncModel\": \"base\",\n \"caption\": {\n \"preset\": \"wrap1\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"audio\": {\n \"speed\": 1,\n \"stability\": 0.5,\n \"similarityBoost\": 0.75,\n \"style\": 0,\n \"useSpeakerBoost\": true\n },\n \"webhook\": \"https://yoursite.com/webhook\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hooked.so/v1/project/create/talking-avatar")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"script\": \"Check out this amazing product! It’s super easy to use and I love the results.\",\n \"avatarId\": \"2\",\n \"voiceId\": \"confident_voice_id'\",\n \"lipsyncModel\": \"base\",\n \"caption\": {\n \"preset\": \"wrap1\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"audio\": {\n \"speed\": 1,\n \"stability\": 0.5,\n \"similarityBoost\": 0.75,\n \"style\": 0,\n \"useSpeakerBoost\": true\n },\n \"webhook\": \"https://yoursite.com/webhook\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"videoId": "vid_talking_avatar_abc123xyz",
"projectId": "proj_ugc_abc123xyz",
"status": "STARTED"
},
"message": "Talking avatar successfully created"
}
{
"success": false,
"message": "script: Script must be at least 1 character"
}
{
"success": false,
"message": "avatarId: Avatar \"invalid_id\" not found."
}
Videos
Talking Avatar
Create talking avatar videos with AI avatars
POST
/
v1
/
project
/
create
/
talking-avatar
Create Talking Avatar
curl --request POST \
--url https://api.hooked.so/v1/project/create/talking-avatar \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data @- <<EOF
{
"script": "Check out this amazing product! It’s super easy to use and I love the results.",
"avatarId": "2",
"voiceId": "confident_voice_id'",
"lipsyncModel": "base",
"caption": {
"preset": "wrap1",
"alignment": "bottom",
"disabled": false
},
"audio": {
"speed": 1,
"stability": 0.5,
"similarityBoost": 0.75,
"style": 0,
"useSpeakerBoost": true
},
"webhook": "https://yoursite.com/webhook",
"metadata": {
"campaignId": "campaign_id",
"variant": "A"
}
}
EOFimport requests
url = "https://api.hooked.so/v1/project/create/talking-avatar"
payload = {
"script": "Check out this amazing product! It’s super easy to use and I love the results.",
"avatarId": "2",
"voiceId": "confident_voice_id'",
"lipsyncModel": "base",
"caption": {
"preset": "wrap1",
"alignment": "bottom",
"disabled": False
},
"audio": {
"speed": 1,
"stability": 0.5,
"similarityBoost": 0.75,
"style": 0,
"useSpeakerBoost": True
},
"webhook": "https://yoursite.com/webhook",
"metadata": {
"campaignId": "campaign_id",
"variant": "A"
}
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
script: 'Check out this amazing product! It’s super easy to use and I love the results.',
avatarId: '2',
voiceId: 'confident_voice_id\'',
lipsyncModel: 'base',
caption: {preset: 'wrap1', alignment: 'bottom', disabled: false},
audio: {
speed: 1,
stability: 0.5,
similarityBoost: 0.75,
style: 0,
useSpeakerBoost: true
},
webhook: 'https://yoursite.com/webhook',
metadata: {campaignId: 'campaign_id', variant: 'A'}
})
};
fetch('https://api.hooked.so/v1/project/create/talking-avatar', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.hooked.so/v1/project/create/talking-avatar",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'script' => 'Check out this amazing product! It’s super easy to use and I love the results.',
'avatarId' => '2',
'voiceId' => 'confident_voice_id\'',
'lipsyncModel' => 'base',
'caption' => [
'preset' => 'wrap1',
'alignment' => 'bottom',
'disabled' => false
],
'audio' => [
'speed' => 1,
'stability' => 0.5,
'similarityBoost' => 0.75,
'style' => 0,
'useSpeakerBoost' => true
],
'webhook' => 'https://yoursite.com/webhook',
'metadata' => [
'campaignId' => 'campaign_id',
'variant' => 'A'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.hooked.so/v1/project/create/talking-avatar"
payload := strings.NewReader("{\n \"script\": \"Check out this amazing product! It’s super easy to use and I love the results.\",\n \"avatarId\": \"2\",\n \"voiceId\": \"confident_voice_id'\",\n \"lipsyncModel\": \"base\",\n \"caption\": {\n \"preset\": \"wrap1\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"audio\": {\n \"speed\": 1,\n \"stability\": 0.5,\n \"similarityBoost\": 0.75,\n \"style\": 0,\n \"useSpeakerBoost\": true\n },\n \"webhook\": \"https://yoursite.com/webhook\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.hooked.so/v1/project/create/talking-avatar")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"script\": \"Check out this amazing product! It’s super easy to use and I love the results.\",\n \"avatarId\": \"2\",\n \"voiceId\": \"confident_voice_id'\",\n \"lipsyncModel\": \"base\",\n \"caption\": {\n \"preset\": \"wrap1\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"audio\": {\n \"speed\": 1,\n \"stability\": 0.5,\n \"similarityBoost\": 0.75,\n \"style\": 0,\n \"useSpeakerBoost\": true\n },\n \"webhook\": \"https://yoursite.com/webhook\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.hooked.so/v1/project/create/talking-avatar")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"script\": \"Check out this amazing product! It’s super easy to use and I love the results.\",\n \"avatarId\": \"2\",\n \"voiceId\": \"confident_voice_id'\",\n \"lipsyncModel\": \"base\",\n \"caption\": {\n \"preset\": \"wrap1\",\n \"alignment\": \"bottom\",\n \"disabled\": false\n },\n \"audio\": {\n \"speed\": 1,\n \"stability\": 0.5,\n \"similarityBoost\": 0.75,\n \"style\": 0,\n \"useSpeakerBoost\": true\n },\n \"webhook\": \"https://yoursite.com/webhook\",\n \"metadata\": {\n \"campaignId\": \"campaign_id\",\n \"variant\": \"A\"\n }\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"videoId": "vid_talking_avatar_abc123xyz",
"projectId": "proj_ugc_abc123xyz",
"status": "STARTED"
},
"message": "Talking avatar successfully created"
}
{
"success": false,
"message": "script: Script must be at least 1 character"
}
{
"success": false,
"message": "avatarId: Avatar \"invalid_id\" not found."
}
Try it out! Use the API playground on the right to test the Talking Avatar endpoint directly.
Overview
Talking Avatar creates videos with AI avatars that speak naturally using advanced lipsync technology. This format is ideal for:- Testimonial-style videos
- Social media content
- Personal video messages
- Quick video updates
- Product presentations
Talking Avatar uses AI avatars with voice synthesis and lipsync technology to create realistic videos that feel natural and engaging.
Endpoint
POST /v1/project/create/talking-avatar
Required Fields
string
required
The script for the avatar to speak (1-10,000 characters). Write naturally as if a real person is speaking.
string
required
Avatar ID from
/v1/avatar/list. Choose an avatar that matches your target audience.string
required
Voice ID from
/v1/voice/list. The voice used for the avatar’s speech.Optional Fields
string
Video name (max 100 characters)
string
default:"base"
Lipsync model quality:
base: Standard quality, faster processingpro: Higher quality, more accurate synchronization
string
Music ID from
/v1/music/list for background music.object
Caption settings for the video
Show Caption Object
Show Caption Object
string
default:"tiktok"
Caption preset style. Available presets:
default, beast, umi, tiktok, wrap1, wrap2, ariel, hooked, classic, active, bubble, glass, comic, glow, pastel, neon, retroTV, red, marker, modern, blue, vivid.string
default:"bottom"
Caption position on the video:
top, middle, or bottomboolean
default:"false"
Set to
true to hide captions on the videoobject
Voice audio settings
Show Audio Settings Object
Show Audio Settings Object
number
default:"1"
Voice speed multiplier (0.7 to 1.2)
number
default:"0.5"
Voice stability (0 to 1). Higher values produce more consistent speech.
number
default:"0.75"
Voice similarity boost (0 to 1). Higher values make the voice more similar to the original.
number
default:"0"
Voice style exaggeration (0 to 1).
boolean
default:"true"
Enable speaker boost for clearer audio.
string
HTTPS URL to receive completion notification (max 500 characters). Highly recommended for production use.
object
Custom metadata object (max 5KB). Store any additional data you need to associate with this video.
{
"campaignId": "summer2024",
"customField": "any value"
}
Request Examples
Basic Talking Avatar
{
"script": "Check out this amazing product! It's super easy to use and I love the results.",
"avatarId": "2",
"voiceId": "confident_voice_id"
}
Complete Talking Avatar
{
"name": "Welcome Video",
"script": "Hey there! Thanks for checking out our channel. We create awesome content every week, so make sure to subscribe and hit that notification bell!",
"avatarId": "2",
"voiceId": "confident_voice_id",
"lipsyncModel": "pro",
"caption": {
"preset": "tiktok",
"alignment": "bottom",
"disabled": false
},
"audio": {
"speed": 1,
"stability": 0.5,
"similarityBoost": 0.75,
"style": 0,
"useSpeakerBoost": true
},
"musicId": "music_upbeat_01",
"webhook": "https://yoursite.com/webhook",
"metadata": {
"channelId": "my-channel",
"videoType": "welcome"
}
}
Response
{
"success": true,
"data": {
"videoId": "vid_talking_avatar_abc123xyz",
"projectId": "proj_ugc_abc123xyz",
"status": "STARTED"
},
"message": "Talking avatar successfully created"
}
{
"success": false,
"message": "script: Script must be at least 1 character"
}
{
"success": false,
"message": "avatarId: Avatar \"invalid_id\" not found."
}
Webhook Notification
When your video is ready, we’ll POST to your webhook URL:Webhook Payload
{
"status": "COMPLETED",
"data": {
"videoId": "vid_talking_avatar_abc123xyz",
"status": "COMPLETED",
"url": "https://cdn.hooked.so/videos/abc123xyz.mp4",
"shareUrl": "https://cdn.hooked.so/shared/abc123xyz.mp4",
"metadata": {
"projectId": "proj_ugc_abc123xyz",
"channelId": "my-channel"
}
},
"message": "Video completed"
}
Your webhook endpoint must return a
200 status code. We’ll retry up to 3 times if the request fails.Lipsync Models
| Model | Description | Processing Time | Best For |
|---|---|---|---|
base | Standard lipsync quality | Faster | Quick videos, testing, lower priority content |
pro | Higher quality lipsync, more accurate synchronization | Slower | Professional content, final productions, high-quality output |
Caption Presets
Available caption presets for thecaption.preset field:
| Preset | Description |
|---|---|
default | Default caption style with bold text and shadow effects |
beast | Bold uppercase style with Komika font |
umi | Yellow glowing text style |
tiktok | Viral & trendy style, perfect for social media |
wrap1 | Wrapped style with red background highlight |
wrap2 | Wrapped style with blue background highlight (uppercase) |
ariel | Bold uppercase style with purple highlight |
hooked | Brand style with purple background |
classic | Clean, simple captions with black background |
active | Green background with bold text |
bubble | White background bubble style |
glass | Glassmorphic transparency effect |
comic | Comic Sans font with colorful style |
glow | Pink and orange glow effects |
pastel | Soft pastel pink background |
neon | Green neon glow effect |
retroTV | Retro TV style with cyan glow |
red | Red glow effect with white text |
marker | Yellow marker/highlighter style |
modern | Contemporary white background style |
blue | Blue background style |
vivid | Vibrant pink background with uppercase text |
Best Practices
Write Naturally
Scripts should sound conversational. Use contractions, pauses, and natural speech patterns.
Choose the Right Model
Use
base for faster processing, pro for higher quality lipsync synchronization.Match Voice to Avatar
Choose a voice that matches the avatar’s appearance for authenticity.
Fine-tune Audio
Adjust audio settings like speed and stability to match your content style.
Keep It Concise
Shorter videos (15-60 seconds) tend to perform better on social media.
Use Webhooks
Always use webhooks in production instead of polling for video status.
Error Handling
| Error | Description | Solution |
|---|---|---|
script: Script must be at least 1 character | Missing or empty script | Add the script field with your content |
avatarId: Avatar not found | Invalid avatar ID | Use a valid avatar ID from /v1/avatar/list |
voiceId: Voice not found | Invalid voice ID | Use a valid voice ID from /v1/voice/list |
lipsyncModel: Invalid lipsync model | Invalid lipsync model | Use either base or pro |
webhook: Must be a valid HTTPS URL | Invalid webhook URL | Ensure webhook URL starts with https:// |
Not enough credits | Insufficient credits | Top up your account credits |
Next Steps
List Avatars
Browse available avatars
List Voices
Find the perfect voice
List Videos
View all your created videos
Webhooks Guide
Learn how to handle webhook notifications
Authorizations
Body
application/json
The script for the avatar to speak (1-10,000 characters)
Required string length:
1 - 10000Avatar ID from /v1/avatar/list
Maximum string length:
30Voice ID from /v1/voice/list
Maximum string length:
30Video name (max 100 characters)
Maximum string length:
100Music ID from /v1/music/list for background music
Maximum string length:
30Lipsync model
Available options:
base, pro Show child attributes
Show child attributes
Voice audio settings
Show child attributes
Show child attributes
HTTPS URL to receive completion notification
Maximum string length:
500Custom metadata object (max 5KB)