Imagera AI - AI content creation platform for generating images, cloning voices, creating avatars, and enhancing videos. Privacy Policy | Terms

Imagera Video Director API

Cinematic 3–15s text-to-video with multi-shot sequencing and character consistency, up to 4K.

imagera-video-directorVideo generationvideo35–665 credits

Calling the API

Install the client

Nothing to install. The API is plain HTTPS and JSON, so the HTTP client already in your project is the client — fetch, requests, curl. There is no SDK.

Setup your API key

Keys are shown once. Keep yours in an environment variable — never in source control, never in client-side code.

Create an API key
bashEnvironment
# Put the key in your environment, never in source control.
export IMAGERA_API_KEY="ima_sk_YOUR_KEY"

Submit a request

Name the model in the path, send its parameters as the JSON body. The call returns a request_id immediately; generation continues in the background.

javascriptPOST /v1/queue/imagera-video-director
const response = await fetch(
  "https://api.imagera.ai/v1/queue/imagera-video-director",
  {
    method: "POST",
    headers: {
      "Authorization": `Key ${process.env.IMAGERA_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      "multi_prompt": [
        {}
      ],
      "aspect_ratio": "16:9",
      "duration": "3"
    }),
  },
);

// 202 Accepted — the job is queued, not finished.
const job = await response.json();
console.log(job.request_id, job.status);

Authentication

API key

Every request carries your key in the Authorization header. Use the Key scheme.

httpRequest header
Authorization: Key ima_sk_YOUR_KEY

Bearer is accepted as well, so an HTTP client that only speaks that scheme works unchanged — it is the same key, not a different credential. Scheme matching is case-insensitive.

Keys are revocable per integration and stop working within about a minute. Rotate by creating the new key, deploying it, then revoking the old one.

Pricing

35–665 credits per request. The exact cost depends on duration and quality and speed.

Credit cost by duration, quality, speed
DurationQualitySpeedCredits
3standardstandard35
3standardultra-fast35
4standardstandard45
4standardultra-fast45
5standardstandard60
5standardultra-fast60
6standardstandard70
6standardultra-fast70
7standardstandard80
7standardultra-fast80
8standardstandard90
8standardultra-fast90
9standardstandard100
9standardultra-fast100
10standardstandard115
10standardultra-fast115
11standardstandard125
11standardultra-fast125
12standardstandard135
12standardultra-fast135
13standardstandard145
13standardultra-fast145
14standardstandard160
14standardultra-fast160
15standardstandard170
15standardultra-fast170
3professionalstandard45
3professionalultra-fast50
4professionalstandard60
4professionalultra-fast70
5professionalstandard70
5professionalultra-fast85
6professionalstandard85
6professionalultra-fast100
7professionalstandard100
7professionalultra-fast120
8professionalstandard115
8professionalultra-fast135
9professionalstandard125
9professionalultra-fast150
10professionalstandard140
10professionalultra-fast170
11professionalstandard155
11professionalultra-fast185
12professionalstandard170
12professionalultra-fast200
13professionalstandard180
13professionalultra-fast220
14professionalstandard195
14professionalultra-fast235
15professionalstandard210
15professionalultra-fast250
34kstandard135
34kultra-fast35
44kstandard180
44kultra-fast45
54kstandard225
54kultra-fast60
64kstandard270
64kultra-fast70
74kstandard315
74kultra-fast80
84kstandard355
84kultra-fast90
94kstandard400
94kultra-fast100
104kstandard445
104kultra-fast115
114kstandard490
114kultra-fast125
124kstandard535
124kultra-fast135
134kstandard580
134kultra-fast145
144kstandard625
144kultra-fast160
154kstandard665
154kultra-fast170

A failed generation is refunded automatically. API calls draw on the same credit balance as the studios.

Webhooks

Webhooks are the intended way to learn that a generation finished: you submit, we call your server when it is done, and you never hold a connection open or run a polling loop.

Register an endpoint

Endpoints are registered against your account, not passed per request, so a leaked request body cannot redirect your callbacks. Each gets its own signing secret, shown once.

  • • HTTPS only — plaintext http:// is refused.
  • • Must resolve to public internet space. Private, loopback, link-local and cloud metadata addresses are refused.
  • • Ports 443 and 8443.
  • • Redirects are followed up to 3 hops, and every hop is re-validated.
  • • No credentials in the URL.
jsonPOST your endpoint — Imagera-Webhook-Event-Type: generation.completed
{
  "event_id": "evt_7c1f9a3e5b2d4086a1c3e5f7b9d0",
  "event_type": "generation.completed",
  "api_version": "2026-07-31",
  "created_at": "2026-07-31T08:42:17.000Z",
  "request_id": "req_8f3c1a9e2b7d",
  "status": "OK",
  "data": {
    "request_id": "req_8f3c1a9e2b7d",
    "gateway_request_id": "req_8f3c1a9e2b7d",
    "status": "COMPLETED",
    "model": "imagera-video-director",
    "credits_used": 35,
    "timings": {
      "inference_seconds": 4.2
    },
    "videos": [
      {
        "url": "https://example.com/output.mp4",
        "file_name": "output.mp4",
        "content_type": "video/mp4"
      }
    ],
    "output_count": 1
  },
  "error": null
}

Verify the signature

Verify every delivery before you trust it. The body hash is inside the signed string, so a valid signature proves both the sender and the exact bytes.

Use the raw request bytes. Re-serialising parsed JSON changes key order and whitespace and will never reproduce the hash. This is the most common integration failure.
javascriptVerify a delivery
const crypto = require("node:crypto");

function verifyImageraWebhook(rawBody, headers, secrets, toleranceSeconds = 300) {
  const eventId   = headers["imagera-webhook-id"];
  const timestamp = headers["imagera-webhook-timestamp"];
  const header    = headers["imagera-webhook-signature"];
  if (!eventId || !timestamp || !header) return false;

  // Replay bound. Without it a captured request is valid forever.
  if (Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp)) > toleranceSeconds) return false;

  const bodyHash     = crypto.createHash("sha256").update(rawBody).digest("hex");
  const signedString = `v1\n${eventId}\n${timestamp}\n${bodyHash}`;

  // The header may carry SEVERAL signatures during a secret rotation.
  const candidates = header.split(/\s+/)
    .filter((p) => p.startsWith("v1="))
    .map((p) => Buffer.from(p.slice(3), "hex"));

  return [].concat(secrets).some((secret) => {
    const expected = crypto.createHmac("sha256", secret).update(signedString).digest();
    return candidates.some(
      (c) => c.length === expected.length && crypto.timingSafeEqual(c, expected),
    );
  });
}

// Express — note express.raw(), NOT express.json().
app.post("/imagera", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyImageraWebhook(req.body, req.headers, [process.env.IMAGERA_WEBHOOK_SECRET])) {
    return res.status(401).send("bad signature");
  }
  const event = JSON.parse(req.body.toString("utf8"));
  // Dedupe on event.event_id, then acknowledge fast.
  res.status(200).send("ok");
});

Retries and delivery

Delivery is at-least-once, so the same event can arrive twice. Record the event_id values you have processed and ignore repeats — it is stable across every attempt.

Return 2xx as soon as the event is durably queued and do the work afterwards; a handler slower than 10s is retried unnecessarily. Failed attempts back off 1s → 2s → 4s → 8s → 16s, up to 6 attempts. A 4xx other than 408, 425 or 429 is permanent and is not retried.

Reject any delivery whose Imagera-Webhook-Timestamp is more than 300 seconds from your clock — that is the replay bound.

Queue

Every generation is queued. These endpoints are how you reconcile state — after a missed webhook, on restart, or to cancel. Poll sparingly; prefer the webhook.

Submit a request

POST /v1/queue/imagera-video-director returns 202 Accepted. Follow the URLs it gives you rather than rebuilding them.

json202 Accepted
{
  "request_id": "req_8f3c1a9e2b7d",
  "status": "IN_QUEUE",
  "model": "imagera-video-director",
  "status_url": "https://api.imagera.ai/v1/queue/requests/req_8f3c1a9e2b7d/status",
  "response_url": "https://api.imagera.ai/v1/queue/requests/req_8f3c1a9e2b7d",
  "cancel_url": "https://api.imagera.ai/v1/queue/requests/req_8f3c1a9e2b7d/cancel"
}

Fetch request status

Status is one of IN_QUEUE, IN_PROGRESS, COMPLETED, FAILED, CANCELLED. The last three are terminal — stop asking once you see one.

javascript/v1/queue/requests/{id}/status
// Prefer the status_url the submit response gave you.
const res = await fetch(job.status_url, {
  headers: { "Authorization": `Key ${process.env.IMAGERA_API_KEY}` },
});
const { status } = await res.json();

Get the result

Once the status is COMPLETED, fetch the result from response_url. Errors come back as application/problem+json documents with a machine-readable code, so you can branch on the code rather than on message text.

javascript/v1/queue/requests/{id}
const res = await fetch(job.response_url, {
  headers: { "Authorization": `Key ${process.env.IMAGERA_API_KEY}` },
});
const result = await res.json();

Files

Anywhere the schema names a URI you can pass either an inline data URI or a public URL. There is no upload endpoint to call first, and generated outputs come back as URLs you can feed straight into a follow-up request.

Data URI (base64)

Simplest for small inputs. Base64 inflates the payload by about a third, so prefer a URL for anything large.

javascriptInline upload
import { readFile } from "node:fs/promises";

const bytes = await readFile("./input.jpg");
const dataUri = `data:image/jpeg;base64,${bytes.toString("base64")}`;

// Pass it anywhere the schema expects a URI.
body: JSON.stringify({ image_urls: [dataUri] })

Hosted files (URL)

Any publicly reachable HTTPS URL. It must be readable without authentication — a signed URL works, a private bucket does not.

javascriptHosted input
body: JSON.stringify({"image_urls":["https://example.com/input.jpg"]})

Uploading files

Pass a data URI for small files, or host the file yourself and pass its URL. Nothing else is required.

Schema

Input

multi_promptlist<MultiPrompt>optional

Per-shot prompts for multi-shot generation.

promptstringoptional

Text instruction for the generation.

Max length: 5000

image_urlslist<string>optional

Input or reference image URLs.

Each item must be a URL or data URI

aspect_ratiostringoptional

Output aspect ratio.

Options: 16:9, 9:16, 1:1

Default "16:9"

durationstringoptional

Output length in seconds. Changes the credit price.

Options: 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15

Default "3"

processing_speedstringoptional

Delivery tier. "ultra-fast" routes to the accelerated path and is priced separately (see the `speed` column of this model's price table).

Options: standard, ultra-fast

Default "standard"

qualitystringoptional

Quality tier. Changes the credit price.

Options: standard, professional, 4k

Default "standard"

elementslist<string>optional

Element references to carry into the generation.

modestringoptional

Generation mode.

Options: std, pro

Default "std"

multi_shotsbooleanoptional

Opt in to multi-shot generation. Requires multi_prompt.

nsfw_checkerbooleanoptional

Run the upstream mature-content classifier on the output.

soundbooleanoptional

Generate an audio track alongside the video.

web_searchbooleanoptional

Let the model consult live web results while generating.

Minimal example

jsonRequired only
{
  "multi_prompt": [
    {}
  ],
  "aspect_ratio": "16:9",
  "duration": "3"
}

Full example

jsonAll parameters
{
  "aspect_ratio": "16:9",
  "duration": "3",
  "elements": [
    "example"
  ],
  "image_urls": [
    "https://example.com/input.jpg"
  ],
  "mode": "std",
  "multi_prompt": [
    {}
  ],
  "multi_shots": false,
  "nsfw_checker": false,
  "processing_speed": "standard",
  "prompt": "a lighthouse at dusk, long exposure",
  "quality": "standard",
  "sound": false,
  "web_search": false
}

Output

Returns video as URLs, not inline bytes. On the result response they arrive at the top level under videos, alongside request_id, status, model, credits_used and timings.

A webhook delivery wraps that same object one level down, in the event's data field, and adds output_count — so event.data.videos on a callback is result.videos when you fetch it yourself. Only the webhook has a data field.

The output payload is not published as a formal schema, so it is not documented field-by-field here rather than being guessed at. Submit one request and inspect the response to see its exact shape.

Other types

MultiPrompt

promptstringrequired

Max length: 512

durationnumberoptional