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; v1 stays 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:

bash
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:

json
{
  "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 key
  • 402 — billing problem (no subscription, no credits)
  • 404 — resource not found
  • 413 — request body too large
  • 429 — rate limit exceeded
  • 5xx — 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).

bash
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 items
  • has_more — boolean, true if more pages remain
  • next_cursor — pass as cursor on the next request, or null

Account

Information about the account behind the API key.

MethodPathDescription
GET/meAccount info + credit balance
GET/creditsCredit balance breakdown by pool

GET /v1/me

bash
curl https://unrealugc.com/api/v1/me \
  -H "Authorization: Bearer $UNREALUGC_API_KEY"
json
{
  "id": "cu1abc...",
  "email": "you@example.com",
  "name": "Your Name",
  "plan": "PRO",
  "subscriptionStatus": "ACTIVE",
  "credits": 4280
}

GET /v1/credits

json
{
  "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.

MethodPathDescription
GET/modelsList active video models

Response — model object

FieldTypeRequiredDescription
idstringYesModel identifier — pass this to generate_video.videoModel.
namestringYesHuman-readable name.
descriptionstringShort description.
maxDurationSecondsnumberMaximum clip length the model supports.
supportsNativeAudiobooleanYesIf true, you can set useNativeAudio=true and skip voiceId.
aspectRatiosstring[]YesSupported aspect ratios (e.g. ['9:16','16:9','1:1']).
tierstringYesCoarse quality tier — standard, premium, ultra.
json
{
  "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.

MethodPathDescription
GET/creatorsList stock + your custom creators
GET/creators/:idFetch one creator
POST/creatorsCreate a custom creator (after avatar upload)
DELETE/creators/:idDelete a custom creator (stock creators cannot be deleted)

GET /v1/creators

Query params: include (stock or custom, default both), limit (max 250).

bash
curl "https://unrealugc.com/api/v1/creators?include=stock" \
  -H "Authorization: Bearer $UNREALUGC_API_KEY"

Stock creator object

FieldTypeRequiredDescription
idstringYesInternal id — pass to generate_video.creatorId.
slugstringYesStable URL-safe slug — also accepted by generate_video.creatorId.
namestringYesDisplay name.
genderstring'male' | 'female' | null
agenumberApproximate age.
categorystringYesSetting — home, office, outdoor, cafe, etc.
stylestringYesVibe descriptor (e.g. 'Tech-savvy professional').
descriptionstringShort bio.
avatarUrlstringPublic avatar image.
previewVideoUrlstringPublic preview video.
bestForstring[]YesProduct categories this creator suits.
isCustombooleanYesAlways false for stock creators.

Custom creator object

FieldTypeRequiredDescription
idstringYesCustom creator id — pass to generate_video.customCreatorId.
namestringYesName you gave it.
avatarUrlstringPublic avatar URL.
createdAtstringYesISO-8601 timestamp.
isCustombooleanYesAlways true.

POST /v1/creators

Two-step: first call POST /v1/uploads/avatar and PUT the image. Then finalize:

bash
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"
  }'
FieldTypeRequiredDescription
namestringYesDisplay name (2-80 chars).
r2KeystringYesThe 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.

MethodPathDescription
GET/voicesList stock + your custom voices
GET/voices/:idFetch one voice
POST/voicesFinalize a cloned voice (after sample upload)
DELETE/voices/:idDelete a custom voice

Voice object

FieldTypeRequiredDescription
idstringYesVoice id — pass to generate_video.voiceId.
namestringYesDisplay name.
genderstring'male' | 'female' | null
isCustombooleanYesTrue for your cloned voices, false for stock.
previewUrlstringOptional preview audio URL.
createdAtstringISO-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:

bash
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"
  }'
FieldTypeRequiredDescription
namestringYesDisplay name (2+ chars).
r2KeystringYesReturned from POST /v1/uploads/voice-sample.
contentTypestringYesAudio MIME type (e.g. audio/mpeg, audio/wav).
durationSecondsnumberLength of the sample. Recommended: 120-300 seconds.
genderstring'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.

MethodPathDescription
GET/projectsList projects (cursor paginated)
POST/projectsCreate a project
GET/projects/:idFetch one project
PATCH/projects/:idUpdate a project
DELETE/projects/:idSoft-delete a project

Project object

FieldTypeRequiredDescription
idstringYesProject id.
namestringYesDisplay name (1-200 chars).
descriptionstringFree-form description.
productNamestringProduct the project is for.
productDescriptionstringProduct description used as context for AI generation.
productUrlstringLanding URL — optional.
creatorIdstringBound stock creator id.
customCreatorIdstringBound custom creator id.
createdAtstringYesISO-8601 timestamp.
updatedAtstringYesISO-8601 timestamp.

POST /v1/projects

bash
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.

MethodPathDescription
GET/videosList videos (filter by status, projectId)
POST/videosStart a new video generation
GET/videos/:idFetch one video — poll for status
POST/videos/estimateCredit cost estimate before generating

Video object

FieldTypeRequiredDescription
idstringYesVideo id.
statusstringYesDRAFT | GENERATING | RENDERING | COMPLETED | FAILED.
titlestringAI-generated short title (added shortly after creation).
scriptstringVoiceover script as you submitted it.
productNamestringProduct name from the request.
durationSecondsnumberTarget duration in seconds.
aspectRatiostringYes'9:16', '16:9', or '1:1'.
videoModelstringModel id used.
audiostring'native' (model produced audio) | 'voice' (separate voiceover) | null.
voiceIdstringVoice id, if separate voiceover was used.
creatorIdstringStock creator id, if used.
customCreatorIdstringCustom creator id, if used.
projectIdstringYesParent project id.
outputUrlstringFinal MP4 URL — set once status is COMPLETED.
thumbnailUrlstringThumbnail image URL when available.
errorstringFailure reason if status is FAILED.
creditsChargednumberCredits held when generation started.
creditsFinalnumberFinal credit cost after reconciliation (set on COMPLETED).
createdAtstringYesISO-8601 timestamp.
updatedAtstringYesISO-8601 timestamp.

POST /v1/videos

bash
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"
  }'
FieldTypeRequiredDescription
scriptstringYesVoiceover script. 20-2400 chars; ~15 chars/sec.
productNamestringYesUsed for the auto-created project name if no projectId.
creatorIdstringone ofStock creator id or slug.
customCreatorIdstringone ofA custom creator you own (mutually exclusive with creatorId).
videoModelstringModel id from GET /v1/models. Defaults to kling-2.6.
presetSlugstringUse a preset instead of explicit videoModel.
voiceIdstringFrom GET /v1/voices. Required if useNativeAudio=false.
useNativeAudiobooleanUse the video model's native audio. Default true. Only models with supportsNativeAudio=true accept this.
durationSecondsnumberSnaps up to the nearest valid model duration.
aspectRatiostring'9:16', '16:9', or '1:1'.
projectIdstringOptional — we create a project named after productName if omitted.
productImageUrlstringOptional 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.

json
{
  "totalCredits": 280,
  "credits": 4280,
  "canAfford": true
}

GET /v1/videos

Query params: limit (max 100), status, projectId, cursor.

bash
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.

MethodPathDescription
POST/uploads/avatarPresigned PUT URL for a custom creator avatar
POST/uploads/voice-samplePresigned PUT URL for a voice clone sample

POST /v1/uploads/avatar

FieldTypeRequiredDescription
contentTypestringYesimage/jpeg, image/png, or image/webp.
extensionstringYesjpg, jpeg, png, or webp (no leading dot).
json
{
  "uploadUrl": "https://...",
  "r2Key": "custom-creators/.../1234.png",
  "method": "PUT",
  "headers": { "Content-Type": "image/png" },
  "expiresInSeconds": 600
}

Then PUT your file:

bash
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

FieldTypeRequiredDescription
contentTypestringYesaudio/mpeg, audio/wav, audio/webm, audio/ogg, audio/mp4, audio/m4a.
extensionstringYesmp3, 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: