REST API
API Reference
A small JSON-over-HTTPS REST API. Everything you can do in the dashboard is available programmatically.
Overview
The UnrealUGC API is a JSON-over-HTTPS REST API. Everything you can do in the dashboard — generating videos, browsing creators and voices, managing projects — is available programmatically.
- Base URL:
https://unrealugc.com/api/v1 - Auth: Bearer token (see Authentication)
- Content type:
application/json - Versioning: URL-versioned (
v1). Breaking changes ship as new versions;v1stays stable.
Authentication
Create an API key under Settings → API Keys. Keys start with usk_live_ and are shown once at creation — store yours somewhere safe.
Send the key as a Bearer token on every request:
curl https://unrealugc.com/api/v1/me \
-H "Authorization: Bearer usk_live_YOUR_KEY_HERE"API access requires an active subscription or non-zero credit balance — the same paywall as the web dashboard.
Rate limits
Per API key:
- Default: 60 requests / minute
- Generation (
POST /v1/videos): 10 requests / minute
Every response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix seconds). Over the limit returns 429 with Retry-After.
Errors
Errors use a stable envelope:
{
"error": {
"code": "insufficient_credits",
"message": "280 credits required",
"details": { "required": 280 }
}
}HTTP status codes match the error class:
400— invalid request (bad JSON, missing fields, bad values)401— missing or invalid API key402— billing problem (no subscription, no credits)404— resource not found413— request body too large429— rate limit exceeded5xx— server-side issue (retry with backoff)
Pagination
List endpoints support cursor pagination via limit (max 100) and cursor (the id of the last item from the previous page).
curl "https://unrealugc.com/api/v1/videos?limit=20&cursor=cvxyz..." \
-H "Authorization: Bearer $UNREALUGC_API_KEY"List responses include these fields:
data— array of itemshas_more— boolean, true if more pages remainnext_cursor— pass ascursoron the next request, or null
Account
Information about the account behind the API key.
| Method | Path | Description |
|---|---|---|
| GET | /me | Account info + credit balance |
| GET | /credits | Credit balance breakdown by pool |
GET /v1/me
curl https://unrealugc.com/api/v1/me \
-H "Authorization: Bearer $UNREALUGC_API_KEY"{
"id": "cu1abc...",
"email": "you@example.com",
"name": "Your Name",
"plan": "PRO",
"subscriptionStatus": "ACTIVE",
"credits": 4280
}GET /v1/credits
{
"credits": 4280,
"subscriptionCredits": 4000,
"purchasedCredits": 280
}Models
The pool of video models you can pick from. Pass the id of a model into generate_video.videoModel.
| Method | Path | Description |
|---|---|---|
| GET | /models | List active video models |
Response — model object
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Model identifier — pass this to generate_video.videoModel. |
| name | string | Yes | Human-readable name. |
| description | string | — | Short description. |
| maxDurationSeconds | number | — | Maximum clip length the model supports. |
| supportsNativeAudio | boolean | Yes | If true, you can set useNativeAudio=true and skip voiceId. |
| aspectRatios | string[] | Yes | Supported aspect ratios (e.g. ['9:16','16:9','1:1']). |
| tier | string | Yes | Coarse quality tier — standard, premium, ultra. |
{
"object": "list",
"data": [
{
"id": "kling-2.6",
"name": "Kling 2.6",
"description": "Cinematic realism with native audio.",
"maxDurationSeconds": 10,
"supportsNativeAudio": true,
"aspectRatios": ["9:16", "16:9", "1:1"],
"tier": "premium"
}
]
}Creators
Creators are the on-screen personas. UnrealUGC ships with 85+ curated stock creators across age, gender, accent, and vibe — and you can upload your own custom avatars.
| Method | Path | Description |
|---|---|---|
| GET | /creators | List stock + your custom creators |
| GET | /creators/:id | Fetch one creator |
| POST | /creators | Create a custom creator (after avatar upload) |
| DELETE | /creators/:id | Delete a custom creator (stock creators cannot be deleted) |
GET /v1/creators
Query params: include (stock or custom, default both), limit (max 250).
curl "https://unrealugc.com/api/v1/creators?include=stock" \
-H "Authorization: Bearer $UNREALUGC_API_KEY"Stock creator object
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Internal id — pass to generate_video.creatorId. |
| slug | string | Yes | Stable URL-safe slug — also accepted by generate_video.creatorId. |
| name | string | Yes | Display name. |
| gender | string | — | 'male' | 'female' | null |
| age | number | — | Approximate age. |
| category | string | Yes | Setting — home, office, outdoor, cafe, etc. |
| style | string | Yes | Vibe descriptor (e.g. 'Tech-savvy professional'). |
| description | string | — | Short bio. |
| avatarUrl | string | — | Public avatar image. |
| previewVideoUrl | string | — | Public preview video. |
| bestFor | string[] | Yes | Product categories this creator suits. |
| isCustom | boolean | Yes | Always false for stock creators. |
Custom creator object
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Custom creator id — pass to generate_video.customCreatorId. |
| name | string | Yes | Name you gave it. |
| avatarUrl | string | — | Public avatar URL. |
| createdAt | string | Yes | ISO-8601 timestamp. |
| isCustom | boolean | Yes | Always true. |
POST /v1/creators
Two-step: first call POST /v1/uploads/avatar and PUT the image. Then finalize:
curl https://unrealugc.com/api/v1/creators \
-X POST \
-H "Authorization: Bearer $UNREALUGC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Founder avatar",
"r2Key": "custom-creators/.../1234.png"
}'| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Display name (2-80 chars). |
| r2Key | string | Yes | The r2Key returned from POST /v1/uploads/avatar. |
Voices
Voices for the voiceover. UnrealUGC ships with a curated catalog of stock voices and supports cloning your own from a 2-5 minute audio sample.
| Method | Path | Description |
|---|---|---|
| GET | /voices | List stock + your custom voices |
| GET | /voices/:id | Fetch one voice |
| POST | /voices | Finalize a cloned voice (after sample upload) |
| DELETE | /voices/:id | Delete a custom voice |
Voice object
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Voice id — pass to generate_video.voiceId. |
| name | string | Yes | Display name. |
| gender | string | — | 'male' | 'female' | null |
| isCustom | boolean | Yes | True for your cloned voices, false for stock. |
| previewUrl | string | — | Optional preview audio URL. |
| createdAt | string | — | ISO-8601 for custom voices; null for stock. |
POST /v1/voices
Two-step like creators. First POST /v1/uploads/voice-sample, PUT the audio, then finalize:
curl https://unrealugc.com/api/v1/voices \
-X POST \
-H "Authorization: Bearer $UNREALUGC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Founder voice",
"r2Key": "custom-voices/.../1234.mp3",
"contentType": "audio/mpeg",
"durationSeconds": 180,
"gender": "female"
}'| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Display name (2+ chars). |
| r2Key | string | Yes | Returned from POST /v1/uploads/voice-sample. |
| contentType | string | Yes | Audio MIME type (e.g. audio/mpeg, audio/wav). |
| durationSeconds | number | — | Length of the sample. Recommended: 120-300 seconds. |
| gender | string | — | 'male' or 'female'. |
Stock voice ids are referenced directly from generate_video.voiceId. Custom voice ids are also accepted — we resolve both transparently.
Projects
Projects group videos by product or campaign. Optional — if you skipprojectId on a video generation, we create one fromproductName automatically.
| Method | Path | Description |
|---|---|---|
| GET | /projects | List projects (cursor paginated) |
| POST | /projects | Create a project |
| GET | /projects/:id | Fetch one project |
| PATCH | /projects/:id | Update a project |
| DELETE | /projects/:id | Soft-delete a project |
Project object
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Project id. |
| name | string | Yes | Display name (1-200 chars). |
| description | string | — | Free-form description. |
| productName | string | — | Product the project is for. |
| productDescription | string | — | Product description used as context for AI generation. |
| productUrl | string | — | Landing URL — optional. |
| creatorId | string | — | Bound stock creator id. |
| customCreatorId | string | — | Bound custom creator id. |
| createdAt | string | Yes | ISO-8601 timestamp. |
| updatedAt | string | Yes | ISO-8601 timestamp. |
POST /v1/projects
curl https://unrealugc.com/api/v1/projects \
-X POST \
-H "Authorization: Bearer $UNREALUGC_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Q3 Launch", "productName": "Acme Skincare" }'Videos
The main resource. POST /v1/videos kicks off an async generation; the response returns immediately with the video in GENERATING.
| Method | Path | Description |
|---|---|---|
| GET | /videos | List videos (filter by status, projectId) |
| POST | /videos | Start a new video generation |
| GET | /videos/:id | Fetch one video — poll for status |
| POST | /videos/estimate | Credit cost estimate before generating |
Video object
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Video id. |
| status | string | Yes | DRAFT | GENERATING | RENDERING | COMPLETED | FAILED. |
| title | string | — | AI-generated short title (added shortly after creation). |
| script | string | — | Voiceover script as you submitted it. |
| productName | string | — | Product name from the request. |
| durationSeconds | number | — | Target duration in seconds. |
| aspectRatio | string | Yes | '9:16', '16:9', or '1:1'. |
| videoModel | string | — | Model id used. |
| audio | string | — | 'native' (model produced audio) | 'voice' (separate voiceover) | null. |
| voiceId | string | — | Voice id, if separate voiceover was used. |
| creatorId | string | — | Stock creator id, if used. |
| customCreatorId | string | — | Custom creator id, if used. |
| projectId | string | Yes | Parent project id. |
| outputUrl | string | — | Final MP4 URL — set once status is COMPLETED. |
| thumbnailUrl | string | — | Thumbnail image URL when available. |
| error | string | — | Failure reason if status is FAILED. |
| creditsCharged | number | — | Credits held when generation started. |
| creditsFinal | number | — | Final credit cost after reconciliation (set on COMPLETED). |
| createdAt | string | Yes | ISO-8601 timestamp. |
| updatedAt | string | Yes | ISO-8601 timestamp. |
POST /v1/videos
curl https://unrealugc.com/api/v1/videos \
-X POST \
-H "Authorization: Bearer $UNREALUGC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"creatorId": "tech-savvy-female-30",
"script": "I have been using this for two weeks and I am genuinely shocked at how good it is.",
"productName": "Acme Skincare",
"videoModel": "kling-2.6",
"useNativeAudio": true,
"durationSeconds": 10,
"aspectRatio": "9:16"
}'| Field | Type | Required | Description |
|---|---|---|---|
| script | string | Yes | Voiceover script. 20-2400 chars; ~15 chars/sec. |
| productName | string | Yes | Used for the auto-created project name if no projectId. |
| creatorId | string | one of | Stock creator id or slug. |
| customCreatorId | string | one of | A custom creator you own (mutually exclusive with creatorId). |
| videoModel | string | — | Model id from GET /v1/models. Defaults to kling-2.6. |
| presetSlug | string | — | Use a preset instead of explicit videoModel. |
| voiceId | string | — | From GET /v1/voices. Required if useNativeAudio=false. |
| useNativeAudio | boolean | — | Use the video model's native audio. Default true. Only models with supportsNativeAudio=true accept this. |
| durationSeconds | number | — | Snaps up to the nearest valid model duration. |
| aspectRatio | string | — | '9:16', '16:9', or '1:1'. |
| projectId | string | — | Optional — we create a project named after productName if omitted. |
| productImageUrl | string | — | Optional public URL to a product reference image. |
Generation is async. Either poll GET /v1/videos/:id every ~15s, or use webhooks for push updates (recommended).
POST /v1/videos/estimate
Quote the credit cost of a generation before running it. Same input shape as the create endpoint minus the assets.
{
"totalCredits": 280,
"credits": 4280,
"canAfford": true
}GET /v1/videos
Query params: limit (max 100), status, projectId, cursor.
curl "https://unrealugc.com/api/v1/videos?status=COMPLETED&limit=20" \
-H "Authorization: Bearer $UNREALUGC_API_KEY"Uploads
Custom creator avatars and voice cloning samples use a two-step upload flow. The API returns a presigned URL, you PUT the file directly to it, then finalize by calling POST /v1/creators or POST /v1/voices.
| Method | Path | Description |
|---|---|---|
| POST | /uploads/avatar | Presigned PUT URL for a custom creator avatar |
| POST | /uploads/voice-sample | Presigned PUT URL for a voice clone sample |
POST /v1/uploads/avatar
| Field | Type | Required | Description |
|---|---|---|---|
| contentType | string | Yes | image/jpeg, image/png, or image/webp. |
| extension | string | Yes | jpg, jpeg, png, or webp (no leading dot). |
{
"uploadUrl": "https://...",
"r2Key": "custom-creators/.../1234.png",
"method": "PUT",
"headers": { "Content-Type": "image/png" },
"expiresInSeconds": 600
}Then PUT your file:
curl "$UPLOAD_URL" -X PUT --upload-file ./avatar.png \
-H "Content-Type: image/png"Finally, call POST /v1/creators with the r2Key to finalize the creator. Avatars are limited to 5 MB.
POST /v1/uploads/voice-sample
| Field | Type | Required | Description |
|---|---|---|---|
| contentType | string | Yes | audio/mpeg, audio/wav, audio/webm, audio/ogg, audio/mp4, audio/m4a. |
| extension | string | Yes | mp3, wav, webm, ogg, m4a, or mp4. |
Same response shape as the avatar endpoint. Voice samples must be 2-5 minutes of clear speech for best results. Max upload size is 25 MB. Then call POST /v1/voices with the r2Key to finalize.
Webhooks
Subscribe to video.created, video.completed, and video.failed at Settings → Webhooks. Each delivery is signed with HMAC-SHA256 and includes a stable delivery id for idempotency.
Full payload shape, signature verification snippets, and retry behavior: Webhooks docs →
Identifiers
- Stock creator ids accept either the internal id or the slug
- Voice ids: stock voice ids and custom voice ids are both accepted by
generate_video.voiceId; the API resolves transparently - Project and video ids are cuids
- API keys start with
usk_live_ - Webhook signing secrets start with
whsec_
SDKs and integrations
A dedicated SDK is on the way. Until then, the API is plain JSON over HTTPS — any HTTP client works. If you're driving from an LLM agent, use the MCP server instead — it wraps every endpoint as a typed tool. Dedicated guides for popular agents:
- Claude Code (CLI / IDE)
- OpenAI Codex (CLI + Cloud)
- OpenClaw (personal AI agent)
- Hermes Agent (Nous Research)