REST API · v1

Actoria API

Generate photos and videos with your own consistent actors from any language or service. The API exposes the same pipeline as the Actoria studio and the MCP server: standalone photos, first frames, Seedance 2.5 / MiniMax H3 / Wan 3.0 videos, recast, script-to-video and audio — with your library of actors, props and locations, or with images you pass directly. Same account, same tokens, same limits.

Base URL https://actoria.ai/api/v1 · JSON over HTTPS · spec openapi.json (OpenAPI 3.1)

Quick start

  1. Subscribe — generation needs an active plan; reading your library works without one.
  2. Create an API key at Settings → API & MCP. Keys look like act_live_…, are shown once and can be scoped (read, generate, upload) and revoked. Send it as Authorization: Bearer act_live_….
  3. Generate a photo — by default the call waits for the image and returns its URL:
curl -X POST https://actoria.ai/api/v1/photos \
  -H "Authorization: Bearer $ACTORIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Mia on a rooftop at golden hour, cinematic portrait",
    "aspect": "4:5",
    "references": [{ "type": "actor", "id": "act_…" }],
    "wait": true
  }'

The response is 201 with photoId, jobId, status, url, cost and your remaining credits. Start without waiting ("wait": false → 202) and poll:

# started with "wait": false → poll (long-poll up to 120 s per request)
curl "https://actoria.ai/api/v1/jobs/job_…/wait?timeoutS=60" -H "Authorization: Bearer $ACTORIA_API_KEY"

Python

import os, requests

API = "https://actoria.ai/api/v1"
H = {"Authorization": f"Bearer {os.environ['ACTORIA_API_KEY']}"}

r = requests.post(f"{API}/photos", headers=H, json={
    "prompt": "A red lighthouse on a cliff at golden hour",
    "aspect": "16:9",
    "wait": False,
}).json()

job = r["jobId"]
while True:
    w = requests.get(f"{API}/jobs/{job}/wait", headers=H, params={"timeoutS": 60}).json()
    if not w.get("timedOut"):
        break
print(w["status"], w["result"]["url"] if w["result"] else w["job"]["error"])

Node.js

const API = "https://actoria.ai/api/v1";
const H = { Authorization: `Bearer ${process.env.ACTORIA_API_KEY}`, "Content-Type": "application/json" };

const photo = await fetch(`${API}/photos`, {
  method: "POST",
  headers: H,
  body: JSON.stringify({ prompt: "A quiet mountain lake at dawn", aspect: "16:9" }), // wait defaults to true
}).then((r) => r.json());

console.log(photo.status, photo.url); // "succeeded", "https://actoria.ai/f/img_….png"

Authentication and scopes

Endpoints

GET endpoints take their arguments as query parameters (numbers, booleans and comma-separated lists are coerced); POST endpoints take a JSON body. Every field, type and default is documented in the API reference and in the OpenAPI spec; each operation is named after its MCP tool.

Full API reference →

account

Who you are: plan, balance, limits, models and prices.

GET/accountget accountread
GET/modelslist modelsread
GET/estimateestimate costread

library

Actors, props and locations — the reusable references.

GET/actorslist actorsread
GET/propslist propsread
GET/locationslist locationsread
GET/library/searchsearch referencesread
POST/actorscreate actorgenerate
POST/actors/{actorId}/verificationverify actorgenerate

references

Direct images (URL, base64 or multipart) as temporary references.

POST/referencesupload referenceupload
POST/references/uploadUpload a reference image (multipart)upload

photos

Standalone photos — a prompt plus references, no project needed.

POST/photosgenerate photogenerate
GET/photoslist photosread
GET/photos/{photoId}get photoread

generation

First frames and videos inside projects and scenes.

GET/frameslist framesread
GET/videoslist videosread
POST/framesgenerate framegenerate
POST/videosgenerate videogenerate

jobs

Status and long-polling of generation jobs.

GET/jobs/{jobId}get jobread
GET/jobs/{jobId}/waitwait jobread

projects

Projects, scenes, script-to-video and assembly.

GET/projectslist projectsread
GET/projects/{projectId}get projectread
GET/scenes/{sceneId}get sceneread
POST/projects/from-scriptcreate script videogenerate
POST/projects/{projectId}/finishfinish projectgenerate
POST/projects/{projectId}/autorun/stopstop autorungenerate

audio

Speech, sound effects, captions and audio edits.

GET/audio/sourceslist audio sourcesread
POST/audio/speechgenerate speechgenerate
POST/audio/sound-effectsgenerate sound effectgenerate
POST/audio/transcriptionstranscribe audiogenerate
GET/audio/assets/{assetId}get audio assetread
POST/audio/editsapply audio editgenerate

recast

Recast (video → video) with your actors.

GET/recast/sourceslist recast sourcesread
POST/recast/uploadsupload recast videoupload
POST/recastsrecastgenerate

References: saved or direct

Generation endpoints take a references array. Saved library items are referenced by id; direct images come as a public https URL or base64 and are moderated like website uploads (a frame must contain no people; real faces only through verified photo actors created with POST /actors). Frame-kind references are ignored for photos — the photo is the frame.

"references": [
  { "type": "actor", "id": "act_…" },                       // saved actor (GET /actors)
  { "type": "prop", "id": "prp_…" },                        // saved prop
  { "type": "location", "id": "loc_…" },                    // saved location (one per scene)
  { "type": "asset", "id": "ast_…" },                       // from POST /references or /references/upload
  { "type": "frame", "sceneId": "scn_…", "which": "last" }, // continue from your own frame (videos)
  { "type": "url", "url": "https://…/product.png", "kind": "prop", "name": "red sneaker" },
  { "type": "base64", "data": "…", "kind": "location" }     // ≤ 3 MB decoded
]

Jobs and waiting

Webhooks

Add "callbackUrl": "https://…" to any job-creating request (POST /photos, /frames, /videos, /recasts, /actors). When the job reaches a terminal status we POST a JSON event to that URL. The URL must be public https (no private networks, no redirects); the request times out after 10 s and is retried after 1 min, 10 min and 1 h on any non-2xx answer. Recent deliveries and your signing secret are in Settings → Webhooks.

{
  "id": "evt_…",
  "type": "job.succeeded",            // or "job.failed"
  "createdAt": "2026-10-12T10:00:00.000Z",
  "data": {
    "job": { "id": "job_…", "kind": "frame", "status": "succeeded", "cost": 5, "resultUrl": "https://actoria.ai/f/…", "error": null, … },
    "result": { "url": "https://actoria.ai/f/…", "posterUrl": null, "durationS": null },
    "sceneId": "scn_…",
    "projectId": "prj_…",
    "photoId": "prj_…"                // set for photos made with POST /photos
  }
}

Headers: X-Actoria-Event (job.succeeded | job.failed), X-Actoria-Delivery (unique id — use it to deduplicate retries) and X-Actoria-Signature: t=<unix seconds>,v1=<hex> where v1 = HMAC-SHA256(secret, "<t>.<raw body>"). Verify before trusting the payload:

import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(secret, signatureHeader, rawBody) {
  const { t, v1 } = Object.fromEntries(signatureHeader.split(",").map((kv) => kv.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;        // replay window
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return expected.length === v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
import hmac, hashlib, time

def verify(secret: str, signature_header: str, raw_body: bytes) -> bool:
    parts = dict(kv.split("=", 1) for kv in signature_header.split(","))
    if abs(time.time() - int(parts["t"])) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Errors

Every error is JSON with a human-readable error and a stable code; validation errors add issues, payment errors add url, rate limits add retryAfterS and a Retry-After header. Responses are English only.

HTTP/1.1 402 Payment Required
{ "error": "Not enough tokens: need 5, have 2", "code": "paywall", "kind": "tokens", "required": 5, "available": 2, "url": "https://actoria.ai/billing" }
codestatusmeaning
invalid_request422The request body or query doesn't match the schema (see `issues`).
invalid_json400The body is not valid JSON.
invalid_token401Missing, unknown, revoked or expired API key / token.
insufficient_scope403The key lacks the scope the endpoint needs (`scope` says which).
forbidden403The account is blocked.
verify_email403Confirm your e-mail on the website first.
paywall402No active subscription or not enough tokens (`kind`, `required`, `available`, `url`).
not_found404Unknown endpoint, or the object doesn't exist / isn't yours.
scene_busy409The scene already has a running job.
concurrent_limit409Too many videos rendering at once for your plan (`lane`, `limit`).
payload_too_large413The request body exceeds the cap.
prompt_unsafe422The prompt mentions minors or other prohibited content (`term`).
actor_unverified422A photo actor hasn't passed the face check (`url`).
moderation_rejected422The image failed moderation (people in a frame/prop/location reference, explicit content).
model_disabled422The requested model is switched off — see GET /models.
launch_rejected422The generation rules rejected the request (message explains).
rate_limited429Slow down and retry after `retryAfterS` seconds (`Retry-After` header).
provider_unavailable502The generation provider failed; tokens are refunded.
disabled503The API is temporarily switched off by the administrator.
internal500Unexpected error on our side.

Limits and pricing

Security

FAQ

Where do photos go?
Under Photos on the Videos tab of your account — the same place as photos made on the website. photoId is the id of the wrapper project, so the photo can be opened in the studio or deleted from the website.
Can I use my own photos of people?
Only as a photo actor: POST /actors with source: "photo", consent, and the face check link it returns. Direct images with people are rejected.
Is there an SDK?
Not yet — the OpenAPI document works with any client generator. For LLM agents use the MCP server: identical functions, same key.

Base URL: https://actoria.ai/api/v1 · API reference · openapi.json · MCP server · Support