API Documentation
Everything you need to integrate UltimateCreator.ai into your applications.
Getting Started
The UltimateCreator AI API lets you generate cinematic avatar videos, manage digital twins, and distribute content programmatically. All requests use the following base URL:
https://api.ultimatecreator.ai/v1Quick Start
Include your API key in the Authorization header as a Bearer token. Here's a quick example:
curl -X POST https://api.ultimatecreator.ai/v1/video/generate \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: unique-request-id-123" \
-d '{
"prompt": "A tech CEO delivering a keynote on stage",
"duration_seconds": 30,
"avatar_id": "avt_abc123"
}'Authentication
API keys are managed in the Portal dashboard. Each key is scoped to a project and can be rotated at any time without downtime.
Authorization: Bearer <YOUR_API_KEY>Content-Type: application/jsonSecurity Best Practice
Video Generation
Async Processing — Do NOT Hold Connections Open
Create a Video Job
/v1/video/generateSubmits an asynchronous video generation job. Returns immediately with a job ID you can use to track progress.
Request Body
{
"prompt": "A tech CEO delivering a keynote on stage",
"duration_seconds": 30,
"avatar_id": "avt_abc123",
"brand_kit_id": "bk_xyz789",
"resolution": "1080p",
"webhook_url": "https://your-server.com/webhooks/video"
}Response — 202 Accepted
{
"job_id": "job_01HXYZ",
"status": "queued",
"credits_charged": 150,
"estimated_duration_seconds": 240,
"created_at": "2025-03-06T12:00:00Z"
}Idempotency-Key Header
Idempotency-Key header with a unique UUID on every POST request. If a network error occurs and you retry, the API will return the original response instead of creating a duplicate job. Keys expire after 24 hours.Poll Job Status
/v1/video/{job_id}/statusReturns the current status and progress of a video generation job. Poll every 10–30 seconds until status is completed or failed.
{
"job_id": "job_01HXYZ",
"status": "completed",
"progress_percent": 100,
"video_url": "https://cdn.ultimatecreator.ai/v/job_01HXYZ.mp4",
"thumbnail_url": "https://cdn.ultimatecreator.ai/t/job_01HXYZ.jpg",
"duration_seconds": 30,
"credits_charged": 150,
"created_at": "2025-03-06T12:00:00Z",
"completed_at": "2025-03-06T12:04:12Z"
}Status Values
queuedJob received, waiting for processingprocessingVideo is being generatedcompletedVideo is ready for downloadfailedGeneration failed — check error_messagecancelledJob was cancelled by the userCancel a Job
/v1/video/{job_id}/cancelCancels a queued or in-progress job. Credits for unprocessed seconds are refunded automatically.
Webhooks
Configure webhook endpoints to receive real-time notifications when jobs complete, fail, or require action. This is the recommended approach instead of polling.
Configuration
Set your webhook URL in the Portal dashboard under Settings → Webhooks, or include a webhook_url field in your video generation request for per-job callbacks.
Payload Format
{
"event": "video.job.completed",
"timestamp": "2025-03-06T12:04:12Z",
"data": {
"job_id": "job_01HXYZ",
"status": "completed",
"video_url": "https://cdn.ultimatecreator.ai/v/job_01HXYZ.mp4",
"thumbnail_url": "https://cdn.ultimatecreator.ai/t/job_01HXYZ.jpg",
"duration_seconds": 30,
"credits_charged": 150
}
}Event Types
video.job.completedVideo generation finished successfullyvideo.job.failedVideo generation encountered an errorvideo.job.progressProgress update (every 25%)
Signature Verification
Every webhook request includes an X-UC-Signature header containing an HMAC-SHA256 hex digest of the request body, signed with your webhook secret. Always verify this signature before processing.
Node.js
import crypto from "crypto";
function verifyWebhookSignature(body, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(body, "utf8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expected, "hex")
);
}
// Express middleware example
app.post("/webhooks/video", (req, res) => {
const signature = req.headers["x-uc-signature"];
const isValid = verifyWebhookSignature(
req.rawBody, signature, process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).json({ error: "Invalid signature" });
}
const event = req.body;
console.log("Received:", event.event, event.data.job_id);
res.status(200).json({ received: true });
});Python
import hmac
import hashlib
def verify_webhook_signature(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode("utf-8"),
body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route("/webhooks/video", methods=["POST"])
def handle_webhook():
signature = request.headers.get("X-UC-Signature", "")
if not verify_webhook_signature(
request.get_data(), signature, os.environ["WEBHOOK_SECRET"]
):
return {"error": "Invalid signature"}, 401
event = request.get_json()
print(f"Received: {event['event']} for {event['data']['job_id']}")
return {"received": True}, 200Retry Policy & Dead Letter Queue
Failed webhook deliveries (non-2xx response or timeout after 10s) are retried with exponential backoff:
After 3 failed attempts, the event is moved to a Dead Letter Queue (DLQ) visible in the Portal dashboard. You can inspect the payload and manually replay any event.
Avatars
Manage your digital twin avatars with full CRUD operations.
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/avatars | List all avatars |
| POST | /v1/avatars | Create a new avatar |
| GET | /v1/avatars/{id} | Get avatar details |
| PATCH | /v1/avatars/{id} | Update an avatar |
| DELETE | /v1/avatars/{id} | Delete an avatar |
Code Examples
Complete end-to-end examples showing how to submit a video generation job, poll for status, and handle webhook callbacks.
Node.js (fetch)
const API_BASE = "https://api.ultimatecreator.ai/v1";
const API_KEY = process.env.UC_API_KEY;
async function generateVideo() {
// 1. Submit the job
const createRes = await fetch(`${API_BASE}/video/generate`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
prompt: "A tech CEO delivering a keynote on stage",
duration_seconds: 30,
avatar_id: "avt_abc123",
resolution: "1080p",
}),
});
if (!createRes.ok) {
const err = await createRes.json();
throw new Error(`Job creation failed: ${err.message}`);
}
const { job_id } = await createRes.json();
console.log("Job created:", job_id);
// 2. Poll for status
let status = "queued";
while (status !== "completed" && status !== "failed") {
await new Promise((r) => setTimeout(r, 15_000)); // wait 15s
const statusRes = await fetch(
`${API_BASE}/video/${job_id}/status`,
{ headers: { "Authorization": `Bearer ${API_KEY}` } }
);
const job = await statusRes.json();
status = job.status;
console.log(`Status: ${status} (${job.progress_percent ?? 0}%)`);
}
if (status === "failed") {
throw new Error("Video generation failed");
}
// 3. Get the final result
const resultRes = await fetch(
`${API_BASE}/video/${job_id}/status`,
{ headers: { "Authorization": `Bearer ${API_KEY}` } }
);
const result = await resultRes.json();
console.log("Video URL:", result.video_url);
return result;
}
generateVideo().catch(console.error);Python (requests)
import os
import time
import uuid
import requests
API_BASE = "https://api.ultimatecreator.ai/v1"
API_KEY = os.environ["UC_API_KEY"]
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
def generate_video():
# 1. Submit the job
create_res = requests.post(
f"{API_BASE}/video/generate",
headers={**headers, "Idempotency-Key": str(uuid.uuid4())},
json={
"prompt": "A tech CEO delivering a keynote on stage",
"duration_seconds": 30,
"avatar_id": "avt_abc123",
"resolution": "1080p",
},
)
create_res.raise_for_status()
job_id = create_res.json()["job_id"]
print(f"Job created: {job_id}")
# 2. Poll for status
status = "queued"
while status not in ("completed", "failed"):
time.sleep(15)
status_res = requests.get(
f"{API_BASE}/video/{job_id}/status",
headers=headers,
)
status_res.raise_for_status()
job = status_res.json()
status = job["status"]
print(f"Status: {status} ({job.get('progress_percent', 0)}%)")
if status == "failed":
raise RuntimeError("Video generation failed")
# 3. Get the final result
result = requests.get(
f"{API_BASE}/video/{job_id}/status", headers=headers
).json()
print(f"Video URL: {result['video_url']}")
return result
if __name__ == "__main__":
generate_video()Rate Limits
API requests are rate-limited based on your plan tier. When you exceed the limit, requests return 429 Too Many Requests with a Retry-After header indicating how many seconds to wait.
| Plan | Requests / Minute | Concurrent Jobs |
|---|---|---|
| Developer | 60 | 2 |
| Creator Pro | 120 | 5 |
| Agency Studio | 300 | 15 |
| Enterprise | Custom | Custom |
Rate Limit Headers
X-RateLimit-Limit — Maximum requests per window
X-RateLimit-Remaining — Requests remaining in current window
X-RateLimit-Reset — Unix timestamp when the window resets
Error Codes
All error responses follow a consistent format with a machine-readable error code and human-readable message.
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded. Retry after 30 seconds.",
"status": 429,
"retry_after": 30
}
}| Code | Meaning | Description |
|---|---|---|
| 400 | Bad Request | Prompt blocked by moderation filter or malformed request body. |
| 401 | Unauthorized | Missing or invalid API key. |
| 403 | Forbidden | IP not whitelisted or API key lacks required scope. |
| 404 | Not Found | The requested resource does not exist. |
| 422 | Unprocessable Entity | Request body validation failed. Check the error details. |
| 429 | Too Many Requests | Rate limit exceeded. Retry after the Retry-After header value. |
| 500 | Internal Server Error | Something went wrong on our side. Please retry or contact support. |