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 | — | Seconds of video planned from the script — this is what you are charged for. |
| 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 | — | Ignored — length is derived from the script (~15 chars/sec) and split into model-valid clips. Accepted for backwards compatibility. |
| 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. The quote is derived from your script length and the model's valid clip durations, so it matches exactly what POST /v1/videos will charge.
{
"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)