Быстрая речь, 400+ голосов и управление эмоциями
- Голосов: 472
- Языков: 40
- Озвучка текста
Вызывайте его по имени модели minimax/speech-2.8-turbo/text-to-speech через POST /jobs/createTask, от 11 кредитов за 1000 символов (0,11 $). Новые аккаунты получают 30 бесплатных кредитов, чтобы вы могли сначала попробовать в Playground.
minimax/speech-2.8-turbo/text-to-speechPOST https://api.popnow.ai/api/v1/jobs/createTask# 1. Create the task. Keep the Idempotency-Key if you retry after a network error.
export IDEMPOTENCY_KEY="$(uuidgen)"
curl -X POST https://api.popnow.ai/api/v1/jobs/createTask \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d '{
"model": "minimax/speech-2.8-turbo/text-to-speech",
"input": {
"text": "Welcome back. Your files finished rendering a minute ago and are ready to download.",
"voice_id": "Calm_Woman",
"emotion": "neutral",
"language": "auto",
"output_format": "mp3"
}
}'
# 2. Copy data.taskId from the response, then poll until state is succeeded, failed, canceled or expired.
TASK_ID="TASK_ID_FROM_RESPONSE"
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
-H "Authorization: Bearer $POPNOW_API_KEY"// Node 18+ (or any runtime with fetch). Keep the API key on your server.
const BASE = "https://api.popnow.ai/api/v1";
const headers = { Authorization: `Bearer ${process.env.POPNOW_API_KEY}` };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
async function call(path, init = {}) {
const res = await fetch(BASE + path, { ...init, headers: { ...headers, ...init.headers } });
const body = await res.json();
if (body.code !== 200) throw new Error(`${body.code} ${body.msg}`);
return body.data;
}
const { taskId } = await call("/jobs/createTask", {
method: "POST",
headers: { "Content-Type": "application/json", "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({
"model": "minimax/speech-2.8-turbo/text-to-speech",
"input": {
"text": "Welcome back. Your files finished rendering a minute ago and are ready to download.",
"voice_id": "Calm_Woman",
"emotion": "neutral",
"language": "auto",
"output_format": "mp3"
}
}),
});
// Poll with backoff. A finished task may still be copying files: wait while an asset is pending.
let task;
for (let delay = 2000; ; delay = Math.min(delay * 1.5, 15000)) {
await sleep(delay);
task = await call(`/jobs/recordInfo?taskId=${taskId}`);
const copying = (task.output?.assets ?? []).some((a) => a.pending && !a.unavailable);
if (["succeeded", "failed", "canceled", "expired"].includes(task.state) && !copying) break;
}
if (task.state !== "succeeded") throw new Error(`${task.state}: ${task.errorMessage ?? "no detail"}`);
// Each asset has a signed url (no API key needed) that expires at expiresAt: save the file.
for (const asset of task.output.assets) console.log(asset.url);import os, time, uuid, requests
BASE = "https://api.popnow.ai/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['POPNOW_API_KEY']}"}
def call(method, path, **kwargs):
body = requests.request(method, BASE + path, headers={**HEADERS, **kwargs.pop("headers", {})}, timeout=30, **kwargs).json()
if body["code"] != 200:
raise RuntimeError(f'{body["code"]} {body["msg"]}')
return body["data"]
task_id = call(
"POST",
"/jobs/createTask",
headers={"Idempotency-Key": str(uuid.uuid4())}, # reuse it if you retry
json={
"model": "minimax/speech-2.8-turbo/text-to-speech",
"input": {
"text": "Welcome back. Your files finished rendering a minute ago and are ready to download.",
"voice_id": "Calm_Woman",
"emotion": "neutral",
"language": "auto",
"output_format": "mp3"
}
},
)["taskId"]
# Poll with backoff. A finished task may still be copying files: wait while an asset is pending.
delay = 2.0
while True:
time.sleep(delay)
task = call("GET", "/jobs/recordInfo", params={"taskId": task_id})
assets = (task.get("output") or {}).get("assets") or []
copying = any(a.get("pending") and not a.get("unavailable") for a in assets)
if task["state"] in ("succeeded", "failed", "canceled", "expired") and not copying:
break
delay = min(delay * 1.5, 15.0)
if task["state"] != "succeeded":
raise RuntimeError(f'{task["state"]}: {task.get("errorMessage", "no detail")}')
# Each asset has a signed url (no API key needed) that expires at expiresAt: save the file.
for asset in assets:
print(asset["url"])input собран по схеме ниже, поэтому проходит проверку как есть. Замените промпт и ссылки на свои.
Цена
| Вариант | Кредиты за 1000 символов | ≈ USD |
|---|---|---|
| Стандарт | 10,8 | 0,11 $ |
1 кредит = 0,01 $ · Все варианты и цены: GET /models/minimax/speech-2.8-turbo/text-to-speech
Каждая задача округляется вверх до целых кредитов, поэтому один запуск на самом дешёвом уровне стоит 11 кредитов за 1000 символов (0,11 $).
Ограничения
- Длина текста (символов)
- 5,000
- Голоса
- 473
- Языки
- 40
Параметры
Эти поля взяты из той же схемы, по которой мы проверяем каждый запрос. Поля, которых нет в списке, отклоняются.
textstringОбязательноеThe words to be spoken, exactly as written. Sound tags such as (sighs) or (laughs) are performed. Billed per 1,000 characters.
voice_idstringНеобязательноеВыберите один из доступных голосов.
emotionstringНеобязательноеЭмоция в голосе.
speednumberНеобязательноеТемп речи.
pitchintegerНеобязательноеPitch shift in semitones; 0 keeps the voice's own pitch.
volumenumberНеобязательноеLoudness multiplier; 1 is the normal level.
languagestringНеобязательноеЯзык озвучки.
output_formatstringНеобязательноеФормат файла результата.
sample_rateintegerНеобязательноеSample rate in Hz. The model uses 32000 when omitted.
bitrateintegerНеобязательноеMP3 bitrate in bits per second; ignored for other formats. The model uses 128000 when omitted.
audio_channelsstringНеобязательноеMono or stereo output. Mono when omitted.
english_normalizationbooleanНеобязательноеRead numbers, dates and abbreviations in English text the way a person would say them.
pronunciation_dictarray<object>НеобязательноеCustom pronunciations: each alias in the text is read as its pronunciation.
Вложенные поля
pronunciation_dict[].aliasstringОбязательноеThe text as written in the script, without slashes.
pronunciation_dict[].pronunciationstringОбязательноеHow to read it: plain words or IPA in parentheses.
Исходная JSON-схема
{
"type": "object",
"required": [
"text"
],
"properties": {
"text": {
"type": "string",
"maxLength": 5000,
"minLength": 1,
"description": "The words to be spoken, exactly as written. Sound tags such as (sighs) or (laughs) are performed. Billed per 1,000 characters."
},
"pitch": {
"type": "integer",
"default": 0,
"maximum": 12,
"minimum": -12,
"description": "Pitch shift in semitones; 0 keeps the voice's own pitch."
},
"speed": {
"type": "number",
"default": 1,
"maximum": 2,
"minimum": 0.5,
"description": "Speaking rate; 1 is the voice's normal pace."
},
"volume": {
"type": "number",
"default": 1,
"maximum": 10,
"minimum": 0.1,
"description": "Loudness multiplier; 1 is the normal level."
},
"bitrate": {
"enum": [
32000,
64000,
128000,
256000
],
"type": "integer",
"description": "MP3 bitrate in bits per second; ignored for other formats. The model uses 128000 when omitted."
},
"emotion": {
"enum": [
"neutral",
"happy",
"sad",
"angry",
"fearful",
"disgusted",
"surprised"
],
"type": "string",
"default": "neutral",
"description": "Emotion of the reading."
},
"language": {
"enum": [
"auto",
"zh",
"yue",
"en",
"ar",
"ru",
"es",
"fr",
"pt",
"de",
"tr",
"nl",
"uk",
"vi",
"id",
"ja",
"it",
"ko",
"th",
"pl",
"ro",
"el",
"cs",
"fi",
"hi",
"bg",
"da",
"he",
"ms",
"fa",
"sk",
"sv",
"hr",
"fil",
"hu",
"no",
"sl",
"ca",
"nn",
"ta",
"af"
],
"type": "string",
"default": "auto",
"description": "Language hint that improves pronunciation; auto detects it from the text."
},
"voice_id": {
"enum": [
"Wise_Woman",
"Friendly_Person",
"Inspirational_girl",
"Deep_Voice_Man",
"Calm_Woman",
"Casual_Guy",
"Lively_Girl",
"Patient_Man",
"Young_Knight",
"Determined_Man",
"Lovely_Girl",
"Decent_Boy",
"Imposing_Manner",
"Elegant_Man",
"Abbess",
"Sweet_Girl_2",
"Exuberant_Girl",
"English_expressive_narrator",
"English_radiant_girl",
"English_magnetic_voiced_man",
"English_compelling_lady1",
"English_Aussie_Bloke",
"English_captivating_female1",
"English_Upbeat_Woman",
"English_Trustworth_Man",
"English_CalmWoman",
"English_UpsetGirl",
"English_Gentle-voiced_man",
"English_Whispering_girl_v3",
"English_Diligent_Man",
"English_Graceful_Lady",
"English_Husky_MetalHead",
"English_ReservedYoungMan",
"English_PlayfulGirl",
"English_ManWithDeepVoice",
"English_GentleTeacher",
"English_MaturePartner",
"English_FriendlyPerson",
"English_MatureBoss",
"English_Debator",
"whisper_man",
"English_Abbess",
"English_LovelyGirl",
"whisper_woman_1",
"English_Steadymentor",
"English_Deep-VoicedGentleman",
"English_DeterminedMan",
"English_Wiselady",
"English_CaptivatingStoryteller",
"English_AttractiveGirl",
"English_DecentYoungMan",
"English_SentimentalLady",
"English_ImposingManner",
"English_SadTeen",
"English_ThoughtfulMan",
"English_PassionateWarrior",
"English_DecentBoy",
"English_WiseScholar",
"English_Soft-spokenGirl",
"English_SereneWoman",
"English_ConfidentWoman",
"English_PatientMan",
"English_Comedian",
"English_GorgeousLady",
"English_BossyLeader",
"English_LovelyLady",
"English_Strong-WilledBoy",
"English_Deep-tonedMan",
"English_StressedLady",
"English_AssertiveQueen",
"English_AnimeCharacter",
"English_Jovialman",
"English_WhimsicalGirl",
"English_CharmingQueen",
"English_Kind-heartedGirl",
"English_FriendlyNeighbor",
"English_Sweet_Female_4",
"English_Magnetic_Male_2",
"English_Lively_Male_11",
"English_Friendly_Female_3",
"English_Steady_Female_1",
"English_Lively_Male_10",
"English_Magnetic_Male_12",
"English_Steady_Female_5",
"English_Insightful_Speaker",
"English_patient_man_v1",
"English_Persuasive_Man",
"English_Explanatory_Man",
"English_intellect_female_1",
"English_energetic_male_1",
"English_witty_female_1",
"English_Lucky_Robot",
"English_Cute_Girl",
"English_Sharp_Commentator",
"English_Honest_Man",
"angry_pirate_1",
"massive_kind_troll",
"movie_trailer_deep",
"peace_and_ease",
"moss_audio_6dc281eb-713c-11f0-a447-9613c873494c",
"moss_audio_c12a59b9-7115-11f0-a447-9613c873494c",
"moss_audio_076697ad-7144-11f0-a447-9613c873494c",
"moss_audio_737a299c-734a-11f0-918f-4e0486034804",
"moss_audio_19dbb103-7350-11f0-ad20-f2bc95e89150",
"moss_audio_7c7e7ae2-7356-11f0-9540-7ef9b4b62566",
"moss_audio_570551b1-735c-11f0-b236-0adeeecad052",
"moss_audio_ad5baf92-735f-11f0-8263-fe5a2fe98ec8",
"moss_audio_cedfd4d2-736d-11f0-99be-fe40dd2a5fe8",
"moss_audio_a0d611da-737c-11f0-ad20-f2bc95e89150",
"moss_audio_4f4172f4-737b-11f0-9540-7ef9b4b62566",
"moss_audio_62ca20b0-7380-11f0-99be-fe40dd2a5fe8",
"conversational_female_1_v1",
"conversational_female_2_v1",
"socialmedia_female_1_v1",
"BritishChild_male_1_v1",
"BritishChild_female_1_v1",
"Chinese (Mandarin)_Reliable_Executive",
"Chinese (Mandarin)_News_Anchor",
"Chinese (Mandarin)_Unrestrained_Young_Man",
"Chinese (Mandarin)_Mature_Woman",
"Arrogant_Miss",
"Chinese (Mandarin)_Kind-hearted_Antie",
"Robot_Armor",
"hunyin_6",
"Chinese (Mandarin)_HK_Flight_Attendant",
"Chinese (Mandarin)_Humorous_Elder",
"Chinese (Mandarin)_Gentleman",
"Chinese (Mandarin)_Warm_Bestie",
"Chinese (Mandarin)_Stubborn_Friend",
"Chinese (Mandarin)_Sweet_Lady",
"Chinese (Mandarin)_Southern_Young_Man",
"Chinese (Mandarin)_Wise_Women",
"Chinese (Mandarin)_Gentle_Youth",
"Chinese (Mandarin)_Warm_Girl",
"Chinese (Mandarin)_Male_Announcer",
"Chinese (Mandarin)_Kind-hearted_Elder",
"Chinese (Mandarin)_Cute_Spirit",
"Chinese (Mandarin)_Radio_Host",
"Chinese (Mandarin)_Lyrical_Voice",
"Chinese (Mandarin)_Straightforward_Boy",
"Chinese (Mandarin)_Sincere_Adult",
"Chinese (Mandarin)_Gentle_Senior",
"Chinese (Mandarin)_Crisp_Girl",
"Chinese (Mandarin)_Pure-hearted_Boy",
"Chinese (Mandarin)_Soft_Girl",
"Chinese (Mandarin)_IntellectualGirl",
"Chinese (Mandarin)_Warm_HeartedGirl",
"Chinese (Mandarin)_Laid_BackGirl",
"Chinese (Mandarin)_ExplorativeGirl",
"Chinese (Mandarin)_Warm-HeartedAunt",
"Chinese (Mandarin)_BashfulGirl",
"Arabic_CalmWoman",
"Arabic_FriendlyGuy",
"Cantonese_ProfessionalHost(F)",
"Cantonese_GentleLady",
"Cantonese_ProfessionalHost(M)",
"Cantonese_PlayfulMan",
"Cantonese_CuteGirl",
"Cantonese_KindWoman",
"Cantonese_Narrator",
"Cantonese_WiselProfessor",
"Cantonese_IndifferentStaff",
"Dutch_kindhearted_girl",
"Dutch_bossy_leader",
"French_Male_Speech_New",
"French_Female_News Anchor",
"French_CasualMan",
"French_MovieLeadFemale",
"French_FemaleAnchor",
"French_MaleNarrator",
"French_Female Journalist",
"French_Female_Speech_New",
"German_FriendlyMan",
"German_SweetLady",
"German_PlayfulMan",
"Indonesian_SweetGirl",
"Indonesian_ReservedYoungMan",
"Indonesian_CharmingGirl",
"Indonesian_CalmWoman",
"Indonesian_ConfidentWoman",
"Indonesian_CaringMan",
"Indonesian_BossyLeader",
"Indonesian_DeterminedBoy",
"Indonesian_GentleGirl",
"Italian_BraveHeroine",
"Italian_Narrator",
"Italian_WanderingSorcerer",
"Italian_DiligentLeader",
"Italian_ReliableMan",
"Italian_AthleticStudent",
"Italian_ArrogantPrincess",
"Japanese_Whisper_Belle",
"Japanese_IntellectualSenior",
"Japanese_DecisivePrincess",
"Japanese_LoyalKnight",
"Japanese_DominantMan",
"Japanese_SeriousCommander",
"Japanese_ColdQueen",
"Japanese_DependableWoman",
"Japanese_GentleButler",
"Japanese_KindLady",
"Japanese_CalmLady",
"Japanese_OptimisticYouth",
"Japanese_GenerousIzakayaOwner",
"Japanese_SportyStudent",
"Japanese_InnocentBoy",
"Japanese_GracefulMaiden",
"Korean_PowerfulGirl",
"Korean_BossyMan",
"Korean_SweetGirl",
"Korean_CheerfulBoyfriend",
"Korean_EnchantingSister",
"Korean_ShyGirl",
"Korean_ReliableSister",
"Korean_StrictBoss",
"Korean_SassyGirl",
"Korean_ChildhoodFriendGirl",
"Korean_PlayboyCharmer",
"Korean_ElegantPrincess",
"Korean_BraveFemaleWarrior",
"Korean_BraveYouth",
"Korean_CalmLady",
"Korean_EnthusiasticTeen",
"Korean_SoothingLady",
"Korean_IntellectualSenior",
"Korean_LonelyWarrior",
"Korean_MatureLady",
"Korean_InnocentBoy",
"Korean_CharmingSister",
"Korean_AthleticStudent",
"Korean_BraveAdventurer",
"Korean_CalmGentleman",
"Korean_WiseElf",
"Korean_CheerfulCoolJunior",
"Korean_DecisiveQueen",
"Korean_ColdYoungMan",
"Korean_MysteriousGirl",
"Korean_QuirkyGirl",
"Korean_ConsiderateSenior",
"Korean_CheerfulLittleSister",
"Korean_DominantMan",
"Korean_AirheadedGirl",
"Korean_ReliableYouth",
"Korean_FriendlyBigSister",
"Korean_GentleBoss",
"Korean_ColdGirl",
"Korean_HaughtyLady",
"Korean_CharmingElderSister",
"Korean_IntellectualMan",
"Korean_CaringWoman",
"Korean_WiseTeacher",
"Korean_ConfidentBoss",
"Korean_AthleticGirl",
"Korean_PossessiveMan",
"Korean_GentleWoman",
"Korean_CockyGuy",
"Korean_ThoughtfulWoman",
"Korean_OptimisticYouth",
"Portuguese_AnxiousMan",
"Portuguese_Matureresearcher",
"Portuguese_Optimisticyouth",
"Portuguese_CuteElf",
"Portuguese_EnergeticGirl",
"Portuguese_FunnyGuy",
"Portuguese_Nuttylady",
"Portuguese_Deep-tonedMan",
"Portuguese_SentimentalLady",
"Portuguese_BossyLeader",
"Portuguese_Wiselady",
"Portuguese_Strong-WilledBoy",
"Portuguese_Deep-VoicedGentleman",
"Portuguese_UpsetGirl",
"Portuguese_PassionateWarrior",
"Portuguese_AnimeCharacter",
"Portuguese_ConfidentWoman",
"Portuguese_AngryMan",
"Portuguese_CaptivatingStoryteller",
"Portuguese_Godfather",
"Portuguese_ReservedYoungMan",
"Portuguese_SmartYoungGirl",
"Portuguese_Kind-heartedGirl",
"Portuguese_Pompouslady",
"Portuguese_Grinch",
"Portuguese_Debator",
"Portuguese_SweetGirl",
"Portuguese_AttractiveGirl",
"Portuguese_ThoughtfulMan",
"Portuguese_PlayfulGirl",
"Portuguese_GorgeousLady",
"Portuguese_LovelyLady",
"Portuguese_SereneWoman",
"Portuguese_SadTeen",
"Portuguese_MaturePartner",
"Portuguese_Comedian",
"Portuguese_NaughtySchoolgirl",
"Portuguese_Narrator",
"Portuguese_ToughBoss",
"Portuguese_Fussyhostess",
"Portuguese_Dramatist",
"Portuguese_Steadymentor",
"Portuguese_Jovialman",
"Portuguese_CharmingQueen",
"Portuguese_SantaClaus",
"Portuguese_Rudolph",
"Portuguese_Arnold",
"Portuguese_CharmingSanta",
"Portuguese_Ghost",
"Portuguese_HumorousElder",
"Portuguese_CalmLeader",
"Portuguese_GentleTeacher",
"Portuguese_EnergeticBoy",
"Portuguese_ReliableMan",
"Portuguese_SereneElder",
"Portuguese_GrimReaper",
"Portuguese_AssertiveQueen",
"Portuguese_WhimsicalGirl",
"Portuguese_StressedLady",
"Portuguese_FriendlyNeighbor",
"Portuguese_CaringGirlfriend",
"Portuguese_PowerfulSoldier",
"Portuguese_FascinatingBoy",
"Portuguese_RomanticHusband",
"Portuguese_StrictBoss",
"Portuguese_InspiringLady",
"Portuguese_PlayfulSpirit",
"Portuguese_ElegantGirl",
"Portuguese_CompellingGirl",
"Portuguese_PowerfulVeteran",
"Portuguese_SensibleManager",
"Portuguese_ThoughtfulLady",
"Portuguese_TheatricalActor",
"Portuguese_FragileBoy",
"Portuguese_ChattyGirl",
"Portuguese_Conscientiousinstructor",
"Portuguese_RationalMan",
"Portuguese_WiseScholar",
"Portuguese_FrankLady",
"Portuguese_DeterminedManager",
"Portuguese_CharmingLady",
"Russian_HandsomeChildhoodFriend",
"Russian_BrightHeroine",
"Russian_AmbitiousWoman",
"Russian_ReliableMan",
"Russian_CrazyQueen",
"Russian_PessimisticGirl",
"Russian_AttractiveGuy",
"Russian_Bad-temperedBoy",
"Spanish_FriendlyNeighbor",
"Spanish_FragileBoy",
"Spanish_UpsetGirl",
"Spanish_Soft-spokenGirl",
"Spanish_CharmingQueen",
"Spanish_Nuttylady",
"Spanish_ElegantGirl",
"Spanish_FascinatingBoy",
"Spanish_FunnyGuy",
"Spanish_PlayfulSpirit",
"Spanish_TheatricalActor",
"Spanish_SereneWoman",
"Spanish_MaturePartner",
"Spanish_CaptivatingStoryteller",
"Spanish_Narrator",
"Spanish_WiseScholar",
"Spanish_Kind-heartedGirl",
"Spanish_DeterminedManager",
"Spanish_BossyLeader",
"Spanish_ReservedYoungMan",
"Spanish_ConfidentWoman",
"Spanish_ThoughtfulMan",
"Spanish_Strong-WilledBoy",
"Spanish_SophisticatedLady",
"Spanish_RationalMan",
"Spanish_AnimeCharacter",
"Spanish_Deep-tonedMan",
"Spanish_Fussyhostess",
"Spanish_SincereTeen",
"Spanish_FrankLady",
"Spanish_Comedian",
"Spanish_Debator",
"Spanish_ToughBoss",
"Spanish_Wiselady",
"Spanish_Steadymentor",
"Spanish_Jovialman",
"Spanish_SantaClaus",
"Spanish_Rudolph",
"Spanish_Intonategirl",
"Spanish_Arnold",
"Spanish_Ghost",
"Spanish_HumorousElder",
"Spanish_EnergeticBoy",
"Spanish_WhimsicalGirl",
"Spanish_StrictBoss",
"Spanish_ReliableMan",
"Spanish_SereneElder",
"Spanish_AngryMan",
"Spanish_AssertiveQueen",
"Spanish_CaringGirlfriend",
"Spanish_PowerfulSoldier",
"Spanish_PassionateWarrior",
"Spanish_ChattyGirl",
"Spanish_RomanticHusband",
"Spanish_CompellingGirl",
"Spanish_PowerfulVeteran",
"Spanish_SensibleManager",
"Spanish_ThoughtfulLady",
"Turkish_CalmWoman",
"Turkish_Trustworthyman",
"Ukrainian_CalmWoman",
"Ukrainian_WiseScholar",
"Vietnamese_Serene_Man",
"Vietnamese_female_4_v1",
"Vietnamese_male_1_v2",
"Vietnamese_kindhearted_girl",
"Thai_Optimistic_girl",
"Thai_male_1_sample8",
"Thai_Tender_Woman",
"Thai_male_2_sample2",
"Thai_female_1_sample1",
"Thai_female_2_sample2",
"Polish_male_1_sample4",
"Polish_male_2_sample3",
"Polish_female_1_sample1",
"Polish_female_2_sample3",
"Romanian_male_1_sample2",
"Romanian_male_2_sample1",
"Romanian_female_1_sample4",
"Romanian_female_2_sample1",
"Greek_female_1_sample1",
"greek_male_1a_v1",
"Greek_female_2_sample3",
"czech_male_1_v1",
"czech_female_5_v7",
"czech_female_2_v2",
"finnish_male_3_v1",
"finnish_female_4_v1",
"finnish_male_1_v2",
"hindi_male_1_v2",
"hindi_female_2_v1",
"hindi_female_1_v2",
"Bulgarian_male_2_v1",
"Bulgarian_female_1_v1",
"Danish_male_1_v1",
"Danish_female_1_v1",
"Hebrew_male_1_v1",
"Hebrew_female_1_v1",
"Malay_male_1_v1",
"Malay_female_1_v1",
"Malay_female_2_v1",
"Persian_male_1_v1",
"Persian_female_1_v1",
"Slovak_male_1_v1",
"Slovak_female_1_v1",
"Swedish_male_1_v1",
"Swedish_female_1_v1",
"Croatian_male_1_v1",
"Croatian_female_1_v1",
"Filipino_male_1_v1",
"Filipino_female_1_v1",
"Hungarian_male_1_v1",
"Hungarian_female_1_v1",
"Norwegian_male_1_v1",
"Norwegian_female_1_v1",
"Slovenian_male_1_v1",
"Slovenian_female_1_v2",
"Catalan_male_1_v1",
"Catalan_female_1_v1",
"Nynorsk_male_1_v1",
"Nynorsk_female_1_v1",
"Tamil_male_1_v1",
"Tamil_female_1_v1",
"Afrikaans_male_1_v1",
"Afrikaans_female_1_v1",
"Chinese (Mandarin)_Sincere_Adult"
],
"type": "string",
"default": "Calm_Woman",
"description": "System voice. The label shows the voice name and, where known, the language it is built for."
},
"sample_rate": {
"enum": [
8000,
16000,
22050,
24000,
32000,
44100
],
"type": "integer",
"description": "Sample rate in Hz. The model uses 32000 when omitted."
},
"output_format": {
"enum": [
"mp3",
"wav",
"flac",
"pcm"
],
"type": "string",
"default": "mp3",
"description": "File format of the result; pcm is headerless 16-bit little-endian samples."
},
"audio_channels": {
"enum": [
"mono",
"stereo"
],
"type": "string",
"description": "Mono or stereo output. Mono when omitted."
},
"pronunciation_dict": {
"type": "array",
"items": {
"type": "object",
"required": [
"alias",
"pronunciation"
],
"properties": {
"alias": {
"type": "string",
"pattern": "^[^/]+$",
"maxLength": 100,
"minLength": 1,
"description": "The text as written in the script, without slashes."
},
"pronunciation": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "How to read it: plain words or IPA in parentheses."
}
},
"additionalProperties": false
},
"maxItems": 100,
"description": "Custom pronunciations: each alias in the text is read as its pronunciation."
},
"english_normalization": {
"type": "boolean",
"default": false,
"description": "Read numbers, dates and abbreviations in English text the way a person would say them."
}
},
"additionalProperties": false
}Настоящий результат MiniMax Speech 2.8 Turbo
(laughs) Oh, you think you can keep up with me? Fine. Try. Just don't say I didn't warn you.
{
"model": "minimax/speech-2.8-turbo/text-to-speech",
"input": {
"text": "(laughs) Oh, you think you can keep up with me? Fine. Try. Just don't say I didn't warn you.",
"voice_id": "Lively_Girl",
"emotion": "happy",
"language": "auto",
"output_format": "mp3"
}
}Ответы, опрос и колбэки
Все модели работают одинаково: создайте задачу, а затем опрашивайте её или дождитесь колбэка.
Сначала узнайте цену
curl -X POST https://api.popnow.ai/api/v1/jobs/quote \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "minimax/speech-2.8-turbo/text-to-speech",
"input": {
"text": "Welcome back. Your files finished rendering a minute ago and are ready to download.",
"voice_id": "Calm_Woman",
"emotion": "neutral",
"language": "auto",
"output_format": "mp3"
}
}'То же тело, что и у createTask. Возвращает ожидаемое и максимально возможное списание и ничего не запускает.
Принято
HTTP/1.1 202 Accepted
{
"code": 200,
"msg": "success",
"data": {
"taskId": "task_01k3m8x9q2z4v7n5p6r8s0t1w3",
"state": "queued"
},
"request_id": "req_example_create"
}HTTP 202 означает, что задача принята, а кредиты зарезервированы, но ещё не списаны. Итоговое списание никогда не превысит резерв.
Проверка статуса
curl "https://api.popnow.ai/api/v1/jobs/recordInfo?taskId=$TASK_ID" \
-H "Authorization: Bearer $POPNOW_API_KEY"Опрашивайте каждые несколько секунд, постепенно увеличивая паузу, и остановитесь, когда state станет succeeded, failed, canceled или expired.
Готовая задача
{
"code": 200,
"msg": "success",
"data": {
"taskId": "task_01k3m8x9q2z4v7n5p6r8s0t1w3",
"model": "minimax/speech-2.8-turbo/text-to-speech",
"state": "succeeded",
"output": {
"assets": [
{
"url": "https://cdn.popnow.ai/tasks/task_01k3m8x9q2z4v7n5p6r8s0t1w3/output.mp3?signature=FROM_RESPONSE",
"expiresAt": "2026-10-17T12:20:00Z",
"mime": "audio/mpeg",
"durationSeconds": 30
}
]
},
"settled": true
},
"request_id": "req_example_record"
}Скачайте файл по url до expiresAt. Нужна новая ссылка позже? Запросите её через POST /common/download-url.
Проверка колбэка
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
import express from "express";
const app = express();
// express.raw keeps the exact bytes that were signed; express.json would not match.
app.post("/hooks/popnow", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.header("X-Webhook-Timestamp");
const signature = req.header("X-Webhook-Signature") ?? "";
const body = JSON.parse(req.body.toString("utf8"));
const taskId = body.data?.taskId ?? body.task_id;
const digest = createHash("sha256").update(req.body).digest("hex");
const expected = createHmac("sha256", process.env.POPNOW_WEBHOOK_SECRET)
.update(`${taskId}.${timestamp}.${digest}`)
.digest("base64");
const ok = expected.length === signature.length && timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
// Check the signature first, then reject anything older than five minutes.
if (!ok || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return res.status(401).end();
res.status(200).end(); // answer fast, then fetch the task and store its files
});Если задан callBackUrl, мы пришлём вам готовую задачу. Прежде чем доверять ей, проверьте подпись по исходному телу запроса.
Отправка локального файла
curl -X POST https://api.popnow.ai/api/v1/common/upload-url \
-H "Authorization: Bearer $POPNOW_API_KEY" \
-H "Content-Type: application/json" \
-d '{"contentType": "image/png", "bytes": 482113}'
# PUT the file bytes to data.uploadUrl with every header in data.headers, then commit it:
curl -X POST https://api.popnow.ai/api/v1/files/$FILE_ID/commit \
-H "Authorization: Bearer $POPNOW_API_KEY"
# The commit returns a popnow://f/... reference. Put it in any image, video or
# audio field of the task input instead of a public https URL.Публичные https-ссылки работают как есть. Локальные файлы сначала проходят через upload-url и commit.