Skip to content
Developers
API docs / Features

MAKEUGC PLATFORM API · DOCS V2

Custom Voice & Text to Speech

Clone or design a reusable voice, then turn scripts into audio.

Workflow

  1. For cloning: prepare an upload, POST the audio to storage, then call clone.
  2. For design: generate previews, choose one and save its preview_token.
  3. Poll until voice_id is available, or choose a stock voice from /video/voices.
  4. Call /speech/generate with that voice_id and a script.
Cloning and saving a designed voice each reserve 1 API credit unit (100 display credits). Upload and preview requests do not create a permanent voice. Speech generation has no additional credit charge.
Use audio samples you are authorized to use. Save requests are not idempotent; do not repeat them automatically.
Send X-Api-Key on every MakeUGC request. POST operations require an active paid subscription and a positive credit balance; custom voice creation also requires a plan with custom voices.
Requests are strict JSON: unknown fields, user_id and webhook_url are rejected. Use polling for jobs; these endpoints do not send webhooks.
The outer status boolean indicates request success. Most jobs use data.status: processing, completed or failed; Upscale documents its own statuses. Poll every 5–10 seconds and require a non-empty result URL (or voice_id) before using output.
API credit units × 100 = website display credits. A timeout, network error or 5xx may occur after acceptance: do not automatically resubmit a paid operation. If submission_unknown is true, check the project or contact support with x-request-id; reserved credits remain held until the result is resolved.

Example IDs and media URLs are placeholders. Replace them with values from your account and accessible media files.

Prepare an audio upload #

POST /custom-voices/upload

Create an owned upload and obtain presigned multipart POST instructions for a voice sample.

POST multipart/form-data directly to data.presigned.url with every returned fields entry and the audio file as file (append file last). Do not send X-Api-Key to storage. Wait for a successful storage response before cloning; do not send raw audio to /custom-voices/clone.
Use a clear recording with one speaker and no background music. Upload URLs are temporary; request a new upload if the storage signature expires.

Request body

No request parameters.

Request example

cURL
curl --fail-with-body --max-time 120 -X POST "https://app.makeugc.ai/api/platform/custom-voices/upload" \
  -H "X-Api-Key: $MAKEUGC_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{}'
JavaScript
// Run on your server with Node.js 20+.
const apiKey = process.env.MAKEUGC_API_KEY;
if (!apiKey) throw new Error("Set MAKEUGC_API_KEY first");

const response = await fetch("https://app.makeugc.ai/api/platform/custom-voices/upload", {
  method: "POST",
  headers: {
    "X-Api-Key": apiKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({}),
  signal: AbortSignal.timeout(120000),
});
const text = await response.text();
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${text} (request ${response.headers.get("x-request-id")})`);
}
const result = JSON.parse(text);
console.log(result);
Python
# Install the requests package before running.
import os
import requests

payload = {

}

response = requests.post(
    "https://app.makeugc.ai/api/platform/custom-voices/upload",
    headers={"X-Api-Key": os.environ["MAKEUGC_API_KEY"]},
    json=payload,
    timeout=120,
)
if not response.ok:
    raise RuntimeError(f"HTTP {response.status_code}: {response.text} "
                       f"(request {response.headers.get('x-request-id')})")
print(response.json())

200 response example

JSON response
{
  "status": true,
  "data": {
    "id": "upload_123",
    "presigned": {
      "url": "https://your-upload-bucket.example",
      "fields": {
        "key": "uploads/sample.mp3",
        "policy": "SIGNED_POLICY"
      }
    }
  }
}

All requests require X-Api-Key. See errors & retries for unsuccessful responses.

Clone an uploaded voice #

POST /custom-voices/clone

Process a sample uploaded through /custom-voices/upload. Save data.id to track voice availability.

Request body

Parameter Description
upload_id
string
Required
upload id input.

Allowed: At least 1 characters; Up to 256 characters

voice_name
string
Required
voice name input.

Allowed: At least 1 characters; Up to 100 characters

Request example

cURL
curl --fail-with-body --max-time 120 -X POST "https://app.makeugc.ai/api/platform/custom-voices/clone" \
  -H "X-Api-Key: $MAKEUGC_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "upload_id": "upload_123",
  "voice_name": "My brand voice"
}'
JavaScript
// Run on your server with Node.js 20+.
const apiKey = process.env.MAKEUGC_API_KEY;
if (!apiKey) throw new Error("Set MAKEUGC_API_KEY first");

const response = await fetch("https://app.makeugc.ai/api/platform/custom-voices/clone", {
  method: "POST",
  headers: {
    "X-Api-Key": apiKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "upload_id": "upload_123",
  "voice_name": "My brand voice"
}),
  signal: AbortSignal.timeout(120000),
});
const text = await response.text();
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${text} (request ${response.headers.get("x-request-id")})`);
}
const result = JSON.parse(text);
console.log(result);
Python
# Install the requests package before running.
import os
import requests

payload = {
    "upload_id": "upload_123",
    "voice_name": "My brand voice"
}

response = requests.post(
    "https://app.makeugc.ai/api/platform/custom-voices/clone",
    headers={"X-Api-Key": os.environ["MAKEUGC_API_KEY"]},
    json=payload,
    timeout=120,
)
if not response.ok:
    raise RuntimeError(f"HTTP {response.status_code}: {response.text} "
                       f"(request {response.headers.get('x-request-id')})")
print(response.json())

200 response example

JSON response
{
  "status": true,
  "data": {
    "id": "upload_123",
    "status": "processing",
    "credits_charged": 1
  }
}

All requests require X-Api-Key. See errors & retries for unsuccessful responses.

Preview a designed voice #

POST /custom-voices/design

Create voice previews from a description or reference image. Choose a preview before saving it.

Each preview_token belongs to your API key's account and expires after one hour. Pass the token unchanged to /custom-voices/save; provider voice IDs and arbitrary preview URLs are not accepted.

Request body

Parameter Description
prompt
string
Optional
prompt input.

Allowed: Up to 1000 characters

image_url
string
Optional
Public HTTP(S) media URL. Private addresses, credentials and data URIs are not accepted.

Request example

cURL
curl --fail-with-body --max-time 120 -X POST "https://app.makeugc.ai/api/platform/custom-voices/design" \
  -H "X-Api-Key: $MAKEUGC_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "prompt": "Warm, confident adult voice with a natural conversational delivery."
}'
JavaScript
// Run on your server with Node.js 20+.
const apiKey = process.env.MAKEUGC_API_KEY;
if (!apiKey) throw new Error("Set MAKEUGC_API_KEY first");

const response = await fetch("https://app.makeugc.ai/api/platform/custom-voices/design", {
  method: "POST",
  headers: {
    "X-Api-Key": apiKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "prompt": "Warm, confident adult voice with a natural conversational delivery."
}),
  signal: AbortSignal.timeout(120000),
});
const text = await response.text();
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${text} (request ${response.headers.get("x-request-id")})`);
}
const result = JSON.parse(text);
console.log(result);
Python
# Install the requests package before running.
import os
import requests

payload = {
    "prompt": "Warm, confident adult voice with a natural conversational delivery."
}

response = requests.post(
    "https://app.makeugc.ai/api/platform/custom-voices/design",
    headers={"X-Api-Key": os.environ["MAKEUGC_API_KEY"]},
    json=payload,
    timeout=120,
)
if not response.ok:
    raise RuntimeError(f"HTTP {response.status_code}: {response.text} "
                       f"(request {response.headers.get('x-request-id')})")
print(response.json())

200 response example

JSON response
{
  "status": true,
  "data": {
    "text": "A short voice preview.",
    "previews": [
      {
        "audio_url": "https://your-cdn.example/voice-preview.mp3",
        "preview_token": "SIGNED_PREVIEW_TOKEN"
      }
    ]
  }
}

All requests require X-Api-Key. See errors & retries for unsuccessful responses.

Save a designed voice #

POST /custom-voices/save

Save a selected preview as a reusable custom voice.

If voice_id is null, poll /custom-voices/status using the full returned id. Design status IDs expire after seven days; after that use /video/voices. A status token cannot be used to save another voice.

Request body

Parameter Description
preview_token
string
Required
Owner-bound token returned by /custom-voices/design; expires after one hour.

Allowed: At least 1 characters; Up to 8000 characters

voice_name
string
Required
voice name input.

Allowed: At least 1 characters; Up to 100 characters

Request example

cURL
curl --fail-with-body --max-time 120 -X POST "https://app.makeugc.ai/api/platform/custom-voices/save" \
  -H "X-Api-Key: $MAKEUGC_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "preview_token": "SIGNED_PREVIEW_TOKEN",
  "voice_name": "My designed voice"
}'
JavaScript
// Run on your server with Node.js 20+.
const apiKey = process.env.MAKEUGC_API_KEY;
if (!apiKey) throw new Error("Set MAKEUGC_API_KEY first");

const response = await fetch("https://app.makeugc.ai/api/platform/custom-voices/save", {
  method: "POST",
  headers: {
    "X-Api-Key": apiKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "preview_token": "SIGNED_PREVIEW_TOKEN",
  "voice_name": "My designed voice"
}),
  signal: AbortSignal.timeout(120000),
});
const text = await response.text();
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${text} (request ${response.headers.get("x-request-id")})`);
}
const result = JSON.parse(text);
console.log(result);
Python
# Install the requests package before running.
import os
import requests

payload = {
    "preview_token": "SIGNED_PREVIEW_TOKEN",
    "voice_name": "My designed voice"
}

response = requests.post(
    "https://app.makeugc.ai/api/platform/custom-voices/save",
    headers={"X-Api-Key": os.environ["MAKEUGC_API_KEY"]},
    json=payload,
    timeout=120,
)
if not response.ok:
    raise RuntimeError(f"HTTP {response.status_code}: {response.text} "
                       f"(request {response.headers.get('x-request-id')})")
print(response.json())

200 response example

JSON response
{
  "status": true,
  "data": {
    "id": "design:SIGNED_STATUS_TOKEN",
    "voice_id": "voice_123",
    "status": "completed",
    "credits_charged": 1
  }
}

All requests require X-Api-Key. See errors & retries for unsuccessful responses.

Get custom voice availability #

GET /custom-voices/status

Resolve an upload ID or a design status ID to a reusable MakeUGC voice_id.

Use the returned voice_id directly, even if the cached /video/voices listing has not refreshed. A request remains processing until a saved voice is available; if it remains pending, contact support with the job ID and x-request-id rather than repeatedly paying for another submission.

Parameters

Parameter Description
id
string · query
Required
id input.

Allowed: At least 1 characters; Up to 8500 characters

Request example

cURL
curl --fail-with-body --max-time 120 -X GET "https://app.makeugc.ai/api/platform/custom-voices/status?id=ID" \
  -H "X-Api-Key: $MAKEUGC_API_KEY"
JavaScript
// Run on your server with Node.js 20+.
const apiKey = process.env.MAKEUGC_API_KEY;
if (!apiKey) throw new Error("Set MAKEUGC_API_KEY first");

const response = await fetch("https://app.makeugc.ai/api/platform/custom-voices/status?id=ID", {
  method: "GET",
  headers: {
    "X-Api-Key": apiKey,
  },
  signal: AbortSignal.timeout(120000),
});
const text = await response.text();
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${text} (request ${response.headers.get("x-request-id")})`);
}
const result = JSON.parse(text);
console.log(result);
Python
# Install the requests package before running.
import os
import requests

response = requests.get(
    "https://app.makeugc.ai/api/platform/custom-voices/status?id=ID",
    headers={"X-Api-Key": os.environ["MAKEUGC_API_KEY"]},
    timeout=120,
)
if not response.ok:
    raise RuntimeError(f"HTTP {response.status_code}: {response.text} "
                       f"(request {response.headers.get('x-request-id')})")
print(response.json())

200 response example

JSON response
{
  "status": true,
  "data": {
    "id": "upload_123",
    "status": "completed",
    "voice_id": "voice_123"
  }
}

All requests require X-Api-Key. See errors & retries for unsuccessful responses.

Generate speech audio #

POST /speech/generate

Synthesize a script with a stock voice or a custom voice owned by your account. Returns an audio URL synchronously.

voice_id is the MakeUGC database id from GET /video/voices, not the voiceId provider identifier. Custom voices must belong to the API key owner.
eleven_v4 uses stability and similarity_boost; its provider ignores the other settings. eleven_v3 snaps stability to 0, 0.5 or 1. OpenAI and Hebrew voices use the provider assigned to that voice; model does not override it.
Download audio_url for permanent storage. No polling or webhook is needed for speech audio.

Request body

Parameter Description
script
string
Required
script input.

Allowed: At least 1 characters; Up to 1500 characters

voice_id
string
Required
MakeUGC voice id from GET /video/voices, not the provider voiceId.

Allowed: At least 1 characters; Up to 256 characters

model
string
Optional
model input.

Allowed: eleven_multilingual_v2 | eleven_v3 | eleven_v4

Default: eleven_v4

settings
object
Optional
settings input.
settings.stability
number
Optional
stability input.

Allowed: Minimum 0; Maximum 1

Default: 0.5

settings.similarity_boost
number
Optional
similarity boost input.

Allowed: Minimum 0; Maximum 1

Default: 0.75

settings.style
number
Optional
style input.

Allowed: Minimum 0; Maximum 1

Default: 0

settings.use_speaker_boost
boolean
Optional
use speaker boost input.

Default: true

settings.speed
number
Optional
speed input.

Allowed: Minimum 0.5; Maximum 3

Default: 1

Request example

cURL
curl --fail-with-body --max-time 120 -X POST "https://app.makeugc.ai/api/platform/speech/generate" \
  -H "X-Api-Key: $MAKEUGC_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
  "script": "Meet your new everyday coffee companion.",
  "voice_id": "voice_123",
  "model": "eleven_v4",
  "settings": {
    "stability": 0.5,
    "similarity_boost": 0.75
  }
}'
JavaScript
// Run on your server with Node.js 20+.
const apiKey = process.env.MAKEUGC_API_KEY;
if (!apiKey) throw new Error("Set MAKEUGC_API_KEY first");

const response = await fetch("https://app.makeugc.ai/api/platform/speech/generate", {
  method: "POST",
  headers: {
    "X-Api-Key": apiKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "script": "Meet your new everyday coffee companion.",
  "voice_id": "voice_123",
  "model": "eleven_v4",
  "settings": {
    "stability": 0.5,
    "similarity_boost": 0.75
  }
}),
  signal: AbortSignal.timeout(120000),
});
const text = await response.text();
if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${text} (request ${response.headers.get("x-request-id")})`);
}
const result = JSON.parse(text);
console.log(result);
Python
# Install the requests package before running.
import os
import requests

payload = {
    "script": "Meet your new everyday coffee companion.",
    "voice_id": "voice_123",
    "model": "eleven_v4",
    "settings": {
        "stability": 0.5,
        "similarity_boost": 0.75
    }
}

response = requests.post(
    "https://app.makeugc.ai/api/platform/speech/generate",
    headers={"X-Api-Key": os.environ["MAKEUGC_API_KEY"]},
    json=payload,
    timeout=120,
)
if not response.ok:
    raise RuntimeError(f"HTTP {response.status_code}: {response.text} "
                       f"(request {response.headers.get('x-request-id')})")
print(response.json())

200 response example

JSON response
{
  "status": true,
  "data": {
    "audio_url": "https://your-cdn.example/speech.mp3",
    "voice_id": "voice_123"
  }
}

All requests require X-Api-Key. See errors & retries for unsuccessful responses.