Docs · v1
Product-in-Hand
Generate Product-in-Hand videos from a product image and either a script or a voice URL. Choose a model route, then poll a shared status endpoint.
POST/product-in-hand/nova-v2/generate
Audio duration max 90 seconds. Cost: 100 per generation.
Parameters
| Name | Required | Description |
|---|---|---|
image_url |
Yes | Product or subject image URL |
script |
If no voice_url | TTS script |
voice_url |
No | Direct audio URL; skips TTS |
voice_id |
If no voice_url | Voice actor id |
project_id |
No | Existing project id |
webhook_url |
No | Status callback URL |
curl -X POST "https://app.makeugc.ai/api/platform/product-in-hand/nova-v2/generate" \
-H "X-Api-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image_url": "https://cdn.example.com/product.png",
"script": "Hold the bottle toward the camera.",
"voice_id": "voice_123"
}'
const response = await fetch("https://app.makeugc.ai/api/platform/product-in-hand/nova-v2/generate", {
method: "POST",
headers: {
"X-Api-Key": process.env.MAKEUGC_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
image_url: "https://cdn.example.com/product.png",
script: "Hold the bottle toward the camera.",
voice_id: "voice_123",
}),
});
import os
import requests
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={
"image_url": "https://cdn.example.com/product.png",
"script": "Hold the bottle toward the camera.",
"voice_id": "voice_123",
},
)
200 response
{
"status": true,
"message": "Video generation started",
"data": { "id": "video_123" }
}
POST/product-in-hand/omi-human/generate
Same payload shape as Nova V2. Audio max 30 seconds. Credits: under 15s 100; 15–30s 200.
Parameters
image_url is required, plus either voice_url or script.
webhook_url is supported.
Request body
{
"image_url": "https://cdn.example.com/product.png",
"script": "Hold the bottle toward the camera.",
"voice_id": "voice_123",
"webhook_url": "https://example.com/webhooks/makeugc"
}
POST/product-in-hand/veo/generate
Credits: scenes.length × (100 fast or 200 pro).
Parameters
| Name | Required | Description |
|---|---|---|
image_url |
Yes | Thumbnail / first-frame URL |
scenes |
Yes | Array of 1–4 objects with script and description |
model |
No | pro or fast |
voice_id |
No | TTS voice id |
end_image_url |
No | Target ending frame |
project_id |
No | Project id |
webhook_url |
No | Status callback URL |
Request body
{
"image_url": "https://cdn.example.com/frame.png",
"model": "fast",
"scenes": [
{ "script": "Hook line", "description": "Product hero, handheld" }
],
"webhook_url": "https://example.com/webhooks/makeugc"
}
GET/product-in-hand/status
Parameters
| Name | In | Required |
|---|---|---|
id |
query | Yes |
Completed response
{
"status": true,
"message": "Video is completed",
"data": {
"status": "completed",
"url": "https://cloudfront.net/signed-url.mp4"
}
}
Failed response
{
"status": true,
"message": "Video is failed",
"data": {
"status": "failed",
"reason": "Request timeout due to high load"
}
}
Questions
Which Product-in-Hand model should I call?
Nova V2 supports up to 90 seconds of audio at a flat credit cost. Omi-Human is capped at 30 seconds with duration-based credits. Veo takes 1–4 scenes and prices by scene count and pro/fast.
How do I poll Product-in-Hand jobs?
All three generate routes return an id. Poll GET /product-in-hand/status?id={video_id} until data.status is completed, processing, or failed.
