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.
durationSecondsnumberSeconds of video planned from the script — this is what you are charged for.
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.
durationSecondsnumberIgnored — length is derived from the script (~15 chars/sec) and split into model-valid clips. Accepted for backwards compatibility.
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. 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.

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: