API Reference
Generate UGC videos, list actors, and check job status via REST API.
Getting Started
Zero to first video in 5 minutes.
1. Get an API Key
Create one in your dashboard settings. Starts with ma_.
2. List Actors
Public endpoint — no API key required.
curl "https://api.agent-media.ai/v1/actors?limit=5"3. Generate a Video
curl -X POST https://api.agent-media.ai/v1/generate/ugc_video \
-H "Authorization: Bearer ma_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"script": "Have you ever struggled with creating video content? This tool changed everything.",
"actor_slug": "sofia",
"tone": "energetic",
"style": "hormozi",
"target_duration": 10
}'4. Poll for Status
curl https://api.agent-media.ai/v1/videos/{job_id} \
-H "Authorization: Bearer ma_YOUR_KEY"
# When status: "completed", video_url has your MP4.Authentication
Generate, status, and account endpoints require a Bearer token. The actor library (/v1/actors) is public and needs no token. Two token types are accepted on protected endpoints:
- API key (
ma_…) — server-to-server calls. Create one in dashboard settings. - Supabase JWT — user-bound session token issued by Supabase Auth. Use this from web apps and especially mobile apps.
Authorization: Bearer ma_YOUR_API_KEY
# or
Authorization: Bearer eyJhbGc... # Supabase JWTMobile App Auth
Building an iOS or Android app? Use Supabase Auth + JWT, not ma_ API keys. A static API key embedded in your app binary can be extracted from the IPA/APK in minutes — treat anything compiled into the client as public.
Recommended flow
- User signs in via Supabase Auth (Apple Sign-In, Google, email/password).
- Supabase issues an access token (JWT) and a refresh token, stored in iOS Keychain / Android Keystore by the SDK.
- App calls
https://api.agent-media.ai/v1/...withAuthorization: Bearer <access_token>. - SDK refreshes the JWT automatically before expiry. On logout, SDK clears tokens.
- Per-user rate limits, credit balance, and quotas all key off the Supabase user id inside the JWT — no extra wiring needed.
Client SDKs
- iOS:
supabase-swift - Android:
supabase-kt
In-app purchases
App Store and Play Store policy require their billing system for digital goods. Use StoreKit 2 (iOS) or Google Play Billing (Android) to sell credit packs and subscriptions in the mobile app, then verify the receipt server-side and credit the user's balance. Don't embed the Stripe checkout flow in the mobile app — Apple will reject the build.
Recommended hardening
- Email verification required before first generation (Supabase Auth setting).
- App Attest (iOS) / Play Integrity (Android) — cryptographically prove requests come from a genuine, unmodified app build. Blocks reverse-engineered API access and emulator/jailbreak abuse. See
/functions/v1/verify-attestation. - HTTPS only, certificate pinning if your threat model warrants it.
What you already get
- Per-user rate limit: 10 generates/min, 60 reads/min — keyed on JWT user id.
- Credit deduction is atomic with job creation; failures refund automatically.
- One concurrent generation per user; later submissions queue server-side.
- 402 + JSON error when the user is out of credits.
/v1/generate/ugc_videoGenerate a UGC-style video from a script or prompt.
Parameters
| Name | Type | Description |
|---|---|---|
| script | string | Narration script (50-3000 chars). Required if no prompt. |
| prompt | string | AI generates script from this. Required if no script. |
| actor_slug | string | AI actor slug. Use GET /v1/actors to browse. |
| tone | enum | energetic, calm, confident, dramatic |
| style | string | Subtitle style (17 options). Default: hormozi |
| target_duration | number | 5, 10, or 15 seconds |
| music | enum | chill, energetic, corporate, dramatic, upbeat |
| aspect_ratio | enum | 9:16, 16:9, 1:1 |
| allow_broll | boolean | Enable B-roll cutaway scenes |
| broll_images | string[] | Image URLs for B-roll (max 10) |
| webhook_url | string | HTTPS URL for completion callback |
| cta | string | End screen CTA (max 100 chars) |
/v1/generate/show_your_appAI actor holds a phone that shows your app screenshot while reading your script, with Hormozi-style word-by-word subtitles burned in. Powered by GPT Image 2 + Seedance 2.0.
Parameters
| Name | Type | Description |
|---|---|---|
| app_screenshot_url | string | Public URL of a vertical (portrait) app screenshot — PNG, JPEG, or WebP. Required. |
| script | string | What the actor reads (5–3000 chars, capped at 3 words/sec × duration). Required. |
| actor_slug | string | Specific actor slug. Random if omitted. |
| duration | number | 5, 10, or 15 seconds. Default 5. |
| subtitle_style | enum | hormozi or none. Default hormozi. |
| webhook_url | string | HTTPS URL for completion callback. |
Example
curl -X POST https://api.agent-media.ai/v1/generate/show_your_app \
-H "Authorization: Bearer ma_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"app_screenshot_url": "https://cdn.example.com/my-app.png",
"script": "You really need to try this — it generates UGC videos in seconds.",
"duration": 5
}'
# Response:
# { "job_id": "...", "actor_slug": "sarah", "actor_random": true, "credits_deducted": 75, "subtitle_style": "hormozi" }Credit cost: 75 flat. Typical runtime: 4–8 minutes.
/v1/generate/laptop_ugc3-scene UGC ad: an AI actor holds an open laptop showing your app, then a scrolling B-roll over-shoulder shot, then a face-only selfie close. Native lip-synced audio + Hormozi-style burnt subtitles. Powered by GPT Image 2 + Seedance 2.0.
Parameters
| Name | Type | Description |
|---|---|---|
| app_screen_recording_url | string | Public URL of a screen recording of your app — mp4 or mov, max 10 MB, max 10 seconds. Provide this OR app_screen_image_url, not both. |
| app_screen_image_url | string | Public URL of a static screenshot — png, jpeg, or webp, max 5 MB. Provide this OR app_screen_recording_url, not both. |
| script | string | Voiceover script (5–3000 chars, capped at 3 words/sec × duration). Auto-split at sentence boundary between two face shots. |
| actor_slug | string | Specific actor slug. Random if omitted. |
| duration | number | 15 or 20 seconds. Default 20. |
| subtitle_style | enum | hormozi or none. Default hormozi. |
| webhook_url | string | HTTPS URL for completion callback. |
Example
curl -X POST https://api.agent-media.ai/v1/generate/laptop_ugc \
-H "Authorization: Bearer ma_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"app_screen_recording_url": "https://cdn.example.com/my-app-demo.mp4",
"script": "Found this AI tool that ships UGC ads in 2 minutes from my terminal. Run it from CLI or Claude Code. It is agent-media.",
"duration": 20
}'
# Response:
# { "job_id": "...", "actor_slug": "mei", "actor_random": true, "credits_deducted": 200, "subtitle_style": "hormozi" }Credit cost: 200 flat. Typical runtime: 8–12 minutes (first job per actor adds ~3 min for pose pre-generation).
/v1/generate/product_acting_ugcGenerate a product-in-hand UGC video: an AI creator holds, presents, or reacts to your product image in a real-world social ad scenario. Subtitles are generated from the final audio with word-level timestamps.
Parameters
| Name | Type | Description |
|---|---|---|
| product_image_url | string | Public URL of the product image. Required. |
| actor_slug | string | Actor slug from GET /v1/actors. Required. |
| actor_variant_id | uuid | Optional specific actor look/variant. |
| product_name | string | Optional product name for prompt and script context. |
| product_description | string | Product context. Required if script is omitted. |
| script | string | Exact words the actor says. Optional if product_description is provided. |
| template | enum | product-in-hand, mirror-selfie, bathroom-reaction, kitchen-counter, car-selfie, couch-review, expert-interview, product-closeup. |
| acting_style | enum | raw-selfie, shocked, angry, excited, dramatic, weird-hook, casual-demo, honest-review. |
| visual_style | string | Extra camera, pose, environment, or framing direction. |
| duration | number | 5, 10, or 15 seconds. Default 5. |
| subtitle_style | enum | hormozi or none. Default hormozi. |
| webhook_url | string | HTTPS URL for completion callback. |
Example
curl -X POST https://api.agent-media.ai/v1/generate/product_acting_ugc \
-H "Authorization: Bearer ma_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_image_url": "https://cdn.example.com/perfume.png",
"actor_slug": "sarah",
"product_name": "Rose Noir",
"product_description": "Premium rose perfume with a warm vanilla dry-down.",
"template": "product-in-hand",
"acting_style": "honest-review",
"duration": 5,
"subtitle_style": "hormozi"
}'
# Response:
# { "job_id": "...", "actor_slug": "sarah", "credits_deducted": 205, "subtitle_style": "hormozi" }Credit cost: 30 credits/sec + 50 frame credits + 5 script credits when script is generated.
Character Video — 3 stepsBuild a fully-realised character clip end-to-end: (1) generate a multi-angle character sheet from a description, an actor, or a portrait; (2) paint a numbered storyboard from beats or a script; (3) render a 720p Seedance 2.0 video using both as references. Each step is async — the route returns 202 with a job_id; poll GET /v1/videos/{job_id} until status="completed". Pass the same session_id UUID across all three calls so step 3 can backstop the scene prompt from step 2.
Step 1 — POST /v1/character/sheet-generate
Generates a 3:2 landscape character reference sheet via gpt-image-2.
| Name | Type | Description |
|---|---|---|
| description | string | Free-text character description (3–400 chars). Provide alone for "describe a character" mode (worker paints a portrait first, then turns it into the sheet). |
| actor_slug | string | Actor from the agent-media library. Mutually exclusive with reference_image_url. List via GET /v1/actors. |
| reference_image_url | string | Public HTTPS URL of a portrait (PNG/JPEG/WebP, ≤5 MB recommended). Mutually exclusive with actor_slug. |
| session_id | uuid | Optional. Same UUID across all 3 steps makes inserts idempotent. |
Credit cost: 20 credits ($0.20) for actor / reference (1 gpt-image-2 call, 75% margin), 35 credits ($0.35) for description-only (paint portrait + sheet = 2 calls, 71% margin). Typical runtime: 2–4 min.
Step 1.5 (optional) — POST /v1/character/storyboard-suggest
Returns 3 distinct beat-sequence options written by Claude Opus 4.7 (different vibes / scenarios). Sync; no credits.
| Name | Type | Description |
|---|---|---|
| actor_slug | string | Library actor whose persona drives the suggestions. Mutually exclusive with character_description. |
| character_description | string | 1–200 chars. Mutually exclusive with actor_slug. |
| vibe | string | Optional 1–200 char vibe note (e.g. "wholesome", "chaotic"). |
| duration | number | 5 or 10. Default 10. |
| n_panels | number | 4–10. Default 6. |
Returns { options: [{ title, beats[] }, …] } — pick one option's beats array and pass it to step 2.
Step 2 — POST /v1/character/storyboard-generate
Paints a numbered storyboard panel grid from beats or a free-text script. Async.
| Name | Type | Description |
|---|---|---|
| character_sheet_url | string | Required. Public HTTPS URL from step 1 (or your own). |
| beats | string[] | 3–10 short beat descriptions, one per panel. Mutually exclusive with script. |
| script | string | Free-text script (10–1500 chars). gpt-image-2 splits it into 4–6 panels itself. Mutually exclusive with beats. |
| ratio | enum | 9:16, 16:9, or 1:1. Default 9:16. |
| session_id | uuid | Same UUID as step 1 for idempotency. |
Credit cost: 20 credits ($0.20) — 75% margin on $0.05 gpt-image-2 cost. Typical runtime: 2–4 min.
Step 3 — POST /v1/generate/character_video
Renders the final clip via Seedance 2.0 reference-to-video (720p locked).
| Name | Type | Description |
|---|---|---|
| character_sheet_url | string | Required. URL from step 1. |
| storyboard_url | string | Required. URL from step 2. |
| action_prompt | string | Optional scene/action description (the location, dialogue, what the character does). When omitted, api-v2 backstops it from the same session's storyboard script/beats — without scene context Seedance picks the location arbitrarily. |
| duration | number | 5 or 10 seconds. Default 10. Max 10 — Seedance i2v quality drops past 10s with multimodal inputs. |
| aspect_ratio | enum | 9:16, 16:9, or 1:1. Default 9:16. |
| generate_audio | boolean | Whether Seedance synthesizes synchronized audio. Default true. |
| session_id | uuid | Same UUID as steps 1+2. |
| webhook_url | string | HTTPS URL for completion callback. |
Credit cost: 350 credits ($3.50) for 5s, 700 credits ($7.00) for 10s — both 72% margin on EvoLink Seedance 720p ($0.199/sec). Typical render: 90–180s.
End-to-end example
# Step 1 — character sheet (returns 202 + job_id)
SESSION=$(uuidgen)
curl -X POST https://api.agent-media.ai/v1/character/sheet-generate \
-H "Authorization: Bearer ma_YOUR_KEY" -H "Content-Type: application/json" \
-d "{\"description\":\"Marco, 35yo Italian chef, white uniform, curly black hair\",\"session_id\":\"$SESSION\"}"
# Poll until completed
curl -H "Authorization: Bearer ma_YOUR_KEY" \
https://api.agent-media.ai/v1/videos/<job_id>
# Step 2 — storyboard from a script
curl -X POST https://api.agent-media.ai/v1/character/storyboard-generate \
-H "Authorization: Bearer ma_YOUR_KEY" -H "Content-Type: application/json" \
-d "{
\"character_sheet_url\": \"<step1 output URL>\",
\"script\": \"Marco walks into his sunlit Brooklyn kitchen, takes a bite of fresh bread, smiles.\",
\"ratio\": \"9:16\",
\"session_id\": \"$SESSION\"
}"
# Step 3 — final video
curl -X POST https://api.agent-media.ai/v1/generate/character_video \
-H "Authorization: Bearer ma_YOUR_KEY" -H "Content-Type: application/json" \
-d "{
\"character_sheet_url\": \"<step1 output URL>\",
\"storyboard_url\": \"<step2 output URL>\",
\"duration\": 10,
\"aspect_ratio\": \"9:16\",
\"session_id\": \"$SESSION\"
}"
# → { "job_id": "...", "credits_deducted": 400 }Don't want to orchestrate three calls? The CLI runs the whole pipeline in one command: agent-media character-video --description "…" --script "…" --sync. MCP tools create_character_sheet, create_storyboard, and create_character_video expose the same steps to Claude Code, Cursor, and other MCP clients.
/v1/generate/saas_reviewSaaS review video — talking head + product screenshots as B-roll. Provide a product_url and the AI writes the script and stitches the review automatically, or pass an explicit script and supply broll_images (1–3 product screenshots) yourself.
Parameters
| Name | Type | Description |
|---|---|---|
| script | string | Narration script (50–3000 chars). Required if no prompt and no product_url. |
| prompt | string | AI generates a script from this. Mutually exclusive with script. Costs 5 extra credits. |
| product_url | string | SaaS product URL. AI fetches context and generates the script. |
| angle | enum | honest, enthusiastic, roast, tutorial, comparison. |
| actor_slug | string | AI actor slug. Use GET /v1/actors. Random if omitted. |
| target_duration | number | 5, 10, or 15 seconds. |
| style | string | Subtitle style (17 options). Default: hormozi. |
| tone | enum | energetic, calm, confident, dramatic. |
| music | enum | chill, energetic, corporate, dramatic, upbeat. |
| cta | string | End-screen CTA (max 100 chars). |
| aspect_ratio | enum | 9:16, 16:9, 1:1. Default 9:16. |
| allow_broll | boolean | Enable AI-generated B-roll cutaway scenes. |
| broll_images | string[] | Up to 10 product screenshot URLs to use as B-roll. |
| webhook_url | string | HTTPS URL for completion callback. |
Example
curl -X POST https://api.agent-media.ai/v1/generate/saas_review \
-H "Authorization: Bearer ma_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"product_url": "https://linear.app",
"angle": "enthusiastic",
"target_duration": 10,
"actor_slug": "sofia"
}'
# Response:
# { "job_id": "...", "status": "submitted", "credits_deducted": 305 }Credit cost: 30 credits/sec + 5 script credits when AI generates the script.
/v1/generate/subtitleBurn animated subtitles onto an existing video. Audio is transcribed with word-level timestamps and rendered in the chosen style.
Parameters
| Name | Type | Description |
|---|---|---|
| video_url | string | Public URL of the source video. Required. |
| style | enum | 17 styles: hormozi (default), minimal, bold, karaoke, clean, tiktok, neon, fire, glow, pop, aesthetic, impact, pastel, electric, boxed, gradient, spotlight. |
Example
curl -X POST https://api.agent-media.ai/v1/generate/subtitle \
-H "Authorization: Bearer ma_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"video_url": "https://cdn.example.com/clip.mp4",
"style": "hormozi"
}'
# Response:
# { "job_id": "...", "status": "submitted", "credits_deducted": 50 }Credit cost: 50 flat per video.
/v1/actorsBrowse the actor library, filter by gender / age / persona / preset, search by name, or look up a single actor by slug. Public endpoint — no API key required. Rate limited to 60 requests/min per IP.
Query parameters
| Param | Values | Description |
|---|---|---|
| gender | female · male | Filter by gender |
| age_range | 18-25 · 26-35 · 36-50 · 51+ | Filter by age range |
| actor_type | Young Adult · Professional · Mom · Elder · Casual | Filter by persona type |
| preset | show_your_app · … | Filter by preset tag |
| search | string | Case-insensitive substring on name |
| slug | string | Single-actor lookup. Returns 404 with similar-slug suggestions when missing. |
| limit | 1–500 (default 500) | Page size — default returns full active library in one call. Ignored when slug is set. |
| offset | integer (default 0) | Page offset (ignored when slug is set) |
Examples
# List all (default page size 50)
curl "https://api.agent-media.ai/v1/actors"
# Filter and paginate
curl "https://api.agent-media.ai/v1/actors?gender=female&age_range=26-35&limit=20"
# Search by name
curl "https://api.agent-media.ai/v1/actors?search=aali"
# Look up a specific actor
curl "https://api.agent-media.ai/v1/actors?slug=aaliyah"Response — list
{
"actors": [
{
"id": "66ac63f6-1be0-4c70-9914-c8ed90208be0",
"slug": "aaliyah",
"name": "Aaliyah",
"gender": "female",
"age": 28,
"age_range": "26-35",
"nationality": "African American",
"actor_type": "Professional",
"style": "Confident, poised, thoughtful presence",
"portrait_url": "https://.../portrait.png",
"green_screen_url": "https://.../green-screen.png",
"voice_id": "cgSgspJ2msm6clMCkdW9",
"voice_gender": "female",
"lip_sync_engine": "aurora",
"preset": null,
"presets": ["show_your_app"],
"bio_occupation": "Financial Wellness Educator",
"bio_story": "...",
"bio_hobbies": ["home espresso", "early morning runs"],
"status": "active"
}
],
"total": 200,
"limit": 50,
"offset": 0
}Response — single (slug lookup)
{ "actor": { "id": "...", "slug": "aaliyah", "name": "Aaliyah", ... } }404 — slug not found
{
"error": {
"code": "ACTOR_NOT_FOUND",
"message": "Actor 'aaliya' not found. Did you mean: aaliyah?",
"similar": ["aaliyah"]
}
}/v1/videos/{jobId}Poll job status. Returns video_url when completed.
{ "job_id": "uuid", "status": "completed", "video_url": "https://...mp4" }Webhooks
When a video generation job completes (or fails), agent-media sends an HTTP POST to your webhook_url. This lets you get notified immediately instead of polling the status endpoint.
How to use
Include webhook_url in your generate request body:
curl -X POST https://api.agent-media.ai/v1/generate/ugc_video \
-H "Authorization: Bearer ma_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"script": "Your narration script here.",
"actor_slug": "sofia",
"webhook_url": "https://example.com/webhooks/agent-media"
}'Payload format
On success, the POST body contains:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"video_url": "https://media.agent-media.ai/videos/550e8400.mp4",
"credits_deducted": 300
}On failure:
{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error_message": "Script exceeded maximum duration for selected actor."
}Retry policy
If your endpoint throws or returns a non-2xx status code, agent-media makes up to 3 total delivery attempts with exponential backoff: a 1 s wait after the first failure and a 2 s wait after the second. After all attempts are exhausted the webhook is abandoned — you can still poll /v1/videos/{job_id} for the result. Only HTTPS URLs are accepted.
Security
Verify that incoming requests originate from agent-media. The simplest approach is to include a shared secret as a query parameter in your webhook URL (e.g. https://example.com/hook?secret=MY_TOKEN) and check it on every request.
Example: what the POST looks like
curl -X POST https://example.com/webhooks/agent-media?secret=MY_TOKEN \
-H "Content-Type: application/json" \
-d '{
"job_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"video_url": "https://media.agent-media.ai/videos/550e8400.mp4",
"credits_deducted": 300
}'Tip: use webhook.site to inspect webhook payloads while developing.
SDKs
TypeScript
import { AgentMedia } from '@agentmedia/sdk';
const client = new AgentMedia({ apiKey: 'ma_xxx' });
// Standard UGC video
const video = await client.createVideo({ script: '...', actor_slug: 'sofia' });
console.log(video.video_url);
// Show Your App — phone holding your app screenshot
const app = await client.createShowYourApp({
app_screenshot_url: 'https://cdn.example.com/my-app.png',
script: 'You really need to try this',
duration: 5,
});
console.log(app.video_url, 'actor:', app.actor_slug);Python
from agent_media import AgentMedia
client = AgentMedia(api_key="ma_xxx")
# Standard UGC video
video = client.create_video(script="...", actor_slug="sofia")
print(video["video_url"])
# Show Your App
app = client.create_show_your_app(
app_screenshot_url="https://cdn.example.com/my-app.png",
script="You really need to try this",
duration=5,
)
print(app["video_url"], "actor:", app["actor_slug"])Interactive API Explorer
Try endpoints below. Enter your API key in the auth field.