MAKEUGC PLATFORM API · DOCS V2
Product in hand
Animate an actor holding your product using audio-driven generation or a sequence of scripted scenes.
Workflow
- Prepare a public image URL showing the actor with the product.
- Choose Nova V2, Omi-Human or Veo and submit its input fields.
- Save data.id and poll the shared Product-in-Hand status endpoint.
Example IDs and media URLs are placeholders. Replace them with values from your account and accessible media files.
Generate with Nova V2 #
/product-in-hand/nova-v2/generate
Up to 90 seconds. Costs 1 API credit unit (100 displayed credits).
Request body
| Parameter | Description |
|---|---|
image_url
string
Required
|
Public URL of the actor/product composition. |
script
string
Optional
|
Required with voice_id when voice_url is omitted.
Allowed: |
voice_url
string
Optional
|
Public audio URL. Takes priority over script-generated audio. |
voice_id
string
Optional
|
Use the id returned by GET /video/voices, not its voiceId field. |
project_id
string
Optional
|
Existing project ID. Omit to create a project automatically. |
webhook_url
string
Optional
|
Public HTTP(S) callback URL. Use status polling as a fallback. |
Request example
curl --fail-with-body --max-time 120 -X POST "https://app.makeugc.ai/api/platform/product-in-hand/nova-v2/generate" \
-H "X-Api-Key: $MAKEUGC_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"image_url": "https://your-cdn.example/actor-with-product.jpg",
"voice_url": "https://your-cdn.example/voice.mp3"
}'
// 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/product-in-hand/nova-v2/generate", {
method: "POST",
headers: {
"X-Api-Key": apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
"image_url": "https://your-cdn.example/actor-with-product.jpg",
"voice_url": "https://your-cdn.example/voice.mp3"
}),
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);
# Install the requests package before running.
import os
import requests
payload = {
"image_url": "https://your-cdn.example/actor-with-product.jpg",
"voice_url": "https://your-cdn.example/voice.mp3"
}
response = requests.post(
"https://app.makeugc.ai/api/platform/product-in-hand/nova-v2/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
{
"status": true,
"message": "Video generation started",
"data": {
"id": "video_123"
}
}
All requests require X-Api-Key. See
errors & retries for unsuccessful
responses.
Generate with Omi-Human #
/product-in-hand/omi-human/generate
Up to 30 seconds. Under 15 seconds costs 1 API credit unit; 15–30 seconds costs 2 (100 / 200 displayed credits).
Request body
| Parameter | Description |
|---|---|
image_url
string
Required
|
Public URL of the actor/product composition. |
script
string
Optional
|
Required with voice_id when voice_url is omitted.
Allowed: |
voice_url
string
Optional
|
Public audio URL. Takes priority over script-generated audio. |
voice_id
string
Optional
|
Use the id returned by GET /video/voices, not its voiceId field. |
project_id
string
Optional
|
Existing project ID. Omit to create a project automatically. |
webhook_url
string
Optional
|
Public HTTP(S) callback URL. Use status polling as a fallback. |
Request example
curl --fail-with-body --max-time 120 -X POST "https://app.makeugc.ai/api/platform/product-in-hand/omi-human/generate" \
-H "X-Api-Key: $MAKEUGC_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"image_url": "https://your-cdn.example/actor-with-product.jpg",
"voice_url": "https://your-cdn.example/voice.mp3"
}'
// 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/product-in-hand/omi-human/generate", {
method: "POST",
headers: {
"X-Api-Key": apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
"image_url": "https://your-cdn.example/actor-with-product.jpg",
"voice_url": "https://your-cdn.example/voice.mp3"
}),
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);
# Install the requests package before running.
import os
import requests
payload = {
"image_url": "https://your-cdn.example/actor-with-product.jpg",
"voice_url": "https://your-cdn.example/voice.mp3"
}
response = requests.post(
"https://app.makeugc.ai/api/platform/product-in-hand/omi-human/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
{
"status": true,
"message": "Video generation started",
"data": {
"id": "video_123"
}
}
All requests require X-Api-Key. See
errors & retries for unsuccessful
responses.
Generate scenes with Veo #
/product-in-hand/veo/generate
Create 1–4 scenes. Each scene costs 1 API credit unit for Fast or 2 for Pro (100 / 200 displayed credits).
Request body
| Parameter | Description |
|---|---|
image_url
string
Required
|
Public actor/product image URL. |
scenes
object[]
Required
|
Scene objects, each containing a non-empty script and optional description.
Allowed: |
scenes[].script
string
Required
|
Spoken script for this scene. |
scenes[].description
string
Optional
|
Visual direction for this scene. |
model
string
Optional
|
Use pro for Veo 3.1 Pro; omit for Fast.
Allowed:
Default: |
end_image_url
string
Optional
|
Optional last frame URL. Used only for a single-scene request. |
voice_id
string
Optional
|
Use the id returned by GET /video/voices, not its voiceId field. |
project_id
string
Optional
|
Existing project ID. Omit to create a project automatically. |
webhook_url
string
Optional
|
Public HTTP(S) callback URL. Use status polling as a fallback. |
Request example
curl --fail-with-body --max-time 120 -X POST "https://app.makeugc.ai/api/platform/product-in-hand/veo/generate" \
-H "X-Api-Key: $MAKEUGC_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"image_url": "https://your-cdn.example/actor-with-product.jpg",
"scenes": [
{
"script": "This is the cup I reach for every morning.",
"description": "Natural light, relaxed delivery."
}
],
"model": "fast"
}'
// 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/product-in-hand/veo/generate", {
method: "POST",
headers: {
"X-Api-Key": apiKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
"image_url": "https://your-cdn.example/actor-with-product.jpg",
"scenes": [
{
"script": "This is the cup I reach for every morning.",
"description": "Natural light, relaxed delivery."
}
],
"model": "fast"
}),
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);
# Install the requests package before running.
import os
import requests
payload = {
"image_url": "https://your-cdn.example/actor-with-product.jpg",
"scenes": [{
"script": "This is the cup I reach for every morning.",
"description": "Natural light, relaxed delivery."
}],
"model": "fast"
}
response = requests.post(
"https://app.makeugc.ai/api/platform/product-in-hand/veo/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
{
"status": true,
"message": "Video generation started",
"data": {
"id": "video_123"
}
}
All requests require X-Api-Key. See
errors & retries for unsuccessful
responses.
Get video status #
/product-in-hand/status
Poll every 5–10 seconds. Stop on failed, or completed with a non-empty URL. The outer status boolean describes the API request; data.status describes the job.
Parameters
| Parameter | Description |
|---|---|
id
string · query
Required
|
Job ID returned by the matching generation endpoint. |
Request example
curl --fail-with-body --max-time 120 -X GET "https://app.makeugc.ai/api/platform/product-in-hand/status?id=ID" \
-H "X-Api-Key: $MAKEUGC_API_KEY"
// 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/product-in-hand/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);
# Install the requests package before running.
import os
import requests
response = requests.get(
"https://app.makeugc.ai/api/platform/product-in-hand/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
{
"status": true,
"message": "Video is completed",
"data": {
"status": "completed",
"url": "https://your-cdn.example/result.mp4"
}
}
All requests require X-Api-Key. See
errors & retries for unsuccessful
responses.