Developer API · v1

The AI Maraya API

Generate video, images and audio from your own app, workflow or bot with a single REST call — the same models that power the Maraya studio and the Telegram bot.

Bearer key

Send Authorization: Bearer YOUR_API_KEY on every request.

Async jobs

Submit, then poll or receive a signed webhook when done.

All media

Video, image and audio models, with image/video/audio references.

Quickstart

POST /v1/generations takes a multipart form (not JSON), so reference files can be attached directly. It returns 202 with the job id.

cURL — text to video
curl -X POST https://www.aimaraya.live/v1/generations \
  -H "Authorization: Bearer $AI_MARAYA_API_KEY" \
  -F category=video \
  -F model=vseed25 \
  -F prompt="A cinematic sunrise above Dubai, slow aerial push-in" \
  -F duration=5s \
  -F aspect_ratio=16:9 \
  -F resolution=720p
JavaScript — with a reference and polling
const form = new FormData();
form.append("category", "video");
form.append("model", "vseed25");
form.append("mode", "refs");
form.append("prompt", "@image1 walks through a neon market at night.");
form.append("duration", "8s");
form.append("aspect_ratio", "16:9");
form.append("resolution", "720p");
form.append("files", imageBlob, "character.jpg");

const job = await fetch("https://www.aimaraya.live/v1/generations", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.AI_MARAYA_API_KEY}` },
  body: form, // do not set Content-Type; fetch adds the multipart boundary
}).then((r) => r.json());

// Poll until done (or pass webhook_url instead)
let status;
do {
  await new Promise((r) => setTimeout(r, 10_000));
  status = await fetch(`https://www.aimaraya.live/v1/generations/${job.id}`, {
    headers: { Authorization: `Bearer ${process.env.AI_MARAYA_API_KEY}` },
  }).then((r) => r.json());
} while (status.status === "queued" || status.status === "running");

console.log(status.status, status.output_url, status.error);

Request fields

categoryrequiredvideo · image · audio
modelrequiredA model id from GET /v1/models (e.g. vseed25, vk30, as_seedream5, as_nano_pro)
promptrequiredWhat to generate. Reference uploaded files with @image1, @video1, @audio1 (see below).
durationvideoe.g. 5s … 30s (per-model range in /v1/models). Ignored for video edits — see below.
aspect_ratiovideo/image16:9 · 9:16 · 1:1 · 4:3 · 3:4 · 21:9 · adaptive
resolutionvideo/image480p · 720p · 1080p (per model)
modeoptionaltext (default) · refs (use reference files) · frames (first/last frame)
filesoptionalReference files, repeat the field per file (images, videos, audio). Up to ~4 MB per request — use chunked uploads for larger.
uploaded_filesoptionalJSON list from chunked uploads: [{"uploadId":"…","position":0}]
image_url / image_urlsoptionalReference images by public HTTPS link instead of uploading: one URL, or a JSON list ["https://…","https://…"]
video_url / video_urlsoptionalReference videos by public HTTPS link (one URL or a JSON list)
audio_url / audio_urlsoptionalReference audio by public HTTPS link (one URL or a JSON list)
slot_kindsframes modeJSON list aligned to files, e.g. ["start","end"] for first/last frame
generate_audiooptionaltrue (default) / false — native audio on supported video models
webhook_urloptionalPublic HTTPS URL to receive a signed completion event
parametersoptionalJSON object of extra options, e.g. {"optimize_prompt": false}

Reference files & @ tags

Attach references with mode=refs. Each type is numbered separately in the order you send them: the 1st image is @image1, the 2nd image @image2, the 1st video @video1, the 1st audio file @audio1. Say in the prompt what each one is for (“@image1 is Mara”, “use the voice from @audio1”). Variants like @img1 or@Image 1 are normalised automatically.

  • Seedance 2.5: up to 30 images, 10 videos and 10 audio files; all videos together and all audio together max 30 seconds.
  • Images: JPG/PNG/WebP, at least ~300px per side. Videos: MP4 recommended; iPhone HEVC/.mov and small videos are converted automatically. Audio: MP3/WAV recommended; M4A/AAC/OGG converted automatically.
  • Use original material. References detected as copyrighted (film/TV clips, commercial songs, celebrity photos) are rejected by the model; the job fails with that reason and the allowance is returned.
  • Per-model limits are in GET /v1/models (caps.max_images, max_videos, max_audio).
cURL — character and location references
curl -X POST https://www.aimaraya.live/v1/generations \
  -H "Authorization: Bearer $AI_MARAYA_API_KEY" \
  -F category=video \
  -F model=vseed25 \
  -F mode=refs \
  -F prompt="@image1 is Mara. @image2 is the yacht deck. Mara walks along @image2 at sunset, rain starting." \
  -F duration=10s \
  -F aspect_ratio=9:16 \
  -F resolution=720p \
  -F files=@mara.jpg \
  -F files=@yacht.jpg
cURL — references by link (no upload)
curl -X POST https://www.aimaraya.live/v1/generations \
  -H "Authorization: Bearer $AI_MARAYA_API_KEY" \
  -F category=video \
  -F model=vseed25 \
  -F mode=refs \
  -F prompt="@image1 walks through @image2 at golden hour." \
  -F duration=10s \
  -F aspect_ratio=9:16 \
  -F resolution=720p \
  -F image_urls='["https://example.com/character.jpg","https://example.com/location.jpg"]'

Video editing

Send a source video as @video1 with an editing instruction — replace, change, remove, add, edit — to edit that video instead of generating a new one (e.g. swap a person for @image1, change the background, remove an object). For an edit, the output follows the source video's length and aspect ratio; the duration and aspect_ratio you send are ignored. Source videos of 4–30 seconds are accepted; under 20 seconds gives the most stable results.

cURL — replace a person in a video
curl -X POST https://www.aimaraya.live/v1/generations \
  -H "Authorization: Bearer $AI_MARAYA_API_KEY" \
  -F category=video \
  -F model=vseed25 \
  -F mode=refs \
  -F prompt="Replace the man on the left in @video1 with @image1. Keep the scene, camera and motion." \
  -F files=@source.mp4 \
  -F files=@actor.jpg

Long prompts & prompt optimisation

There is no practical prompt length limit on Seedance (up to 20,000 characters). However, a video model renders one continuous clip and tends to follow only the first part of a very long, multi-shot prompt. So for video prompts over 2,500 characters we automatically rewrite the prompt into a compact, timestamped shot script ([0-4s] … [4-9s] …, or Shot 1 / Shot 2 on Seedance 2.0) that keeps every shot, every @ tag and every dialogue line — so later shots are no longer skipped. Your original prompt is kept on the job.

To send your prompt exactly as written, add -F parameters='{"optimize_prompt": false}'. Tip: for the best adherence, write your own prompt as timestamped segments and keep each generation to a few shots.

Model-specific hard caps: Kling 3.0 models accept at most 2,500 characters and Suno v5 at most 3,000 — longer prompts fail immediately with a clear message and no allowance used.

Large files (chunked upload)

Request bodies over ~4 MB are rejected at the edge. For larger references (up to 100 MB each) upload in 2 MB chunks, then pass the uploadIds in uploaded_files. position sets where the file sits in the reference order (and therefore its @ number).

Chunked upload
# 1) start
curl -X POST https://www.aimaraya.live/v1/uploads/init \
  -H "Authorization: Bearer $AI_MARAYA_API_KEY" -H "Content-Type: application/json" \
  -d '{"filename":"source.mp4","contentType":"video/mp4","size":38123456}'
# -> {"uploadId":"UPL","chunkSize":2097152,"totalChunks":19}

# 2) send each 2 MB chunk (index 0..totalChunks-1)
curl -X PUT "https://www.aimaraya.live/v1/uploads/UPL/chunk?index=0" \
  -H "Authorization: Bearer $AI_MARAYA_API_KEY" --data-binary @part-0

# 3) finish
curl -X POST https://www.aimaraya.live/v1/uploads/UPL/complete \
  -H "Authorization: Bearer $AI_MARAYA_API_KEY"

# 4) reference it (position = its place in the reference order)
  -F uploaded_files='[{"uploadId":"UPL","position":0}]'

Job status & errors

GET /v1/generations/{id}
{
  "id": "PuTfR6cpcXMAOuqU",
  "status": "completed",          // queued | running | completed | failed | cancelled
  "progress": 100,
  "stage": "Done",
  "model": "vseed25",
  "type": "video",
  "output_url": "/api/files/maraya_video_PuTfR6cpcXMAOuqU.mp4",
  "error": "",                    // plain-language reason when status is "failed"
  "note": "",                     // extra info, e.g. why output settings differ
  "created_at": 1790415692
}

When a job fails, error contains a plain-language reason and any reserved allowance is returned automatically. Common reasons:

  • A reference was flagged as copyrighted or by the content policy — use a different file.
  • The finished video was blocked by the output safety check — tone down the prompt/references.
  • A reference file is too small, too long or unreadable — the message says which.
  • The prompt is too long for that model (Kling 3.0 / Suno).

429 means your key's concurrent-job limit is reached; retry after the Retry-After seconds.

Endpoints

POST/v1/generationsQueue an image, video or audio generation (multipart form).
GET/v1/generations/{id}Status, progress and output URL of a job.
GET/v1/generationsList recent generations created by this API key.
POST/v1/generations/{id}/cancelCancel a queued or running generation.
GET/v1/modelsAvailable models, durations, resolutions and reference limits.
POST/v1/uploads/initStart a chunked upload for reference files over 4 MB.
PUT/v1/uploads/{id}/chunk?index=NUpload one chunk (raw bytes).
POST/v1/uploads/{id}/completeFinish the upload; returns nothing to store — reuse the uploadId.

Rate limits

Each key has a concurrent-job limit. Normal queue, model availability and service-protection limits apply so one integration cannot block other active jobs.

Authentication

Send the key as Authorization: Bearer YOUR_API_KEY (or X-API-Key). Keys are shown only once when issued, stored hashed, and can be revoked immediately.

Signed completion webhooks

Add a public HTTPS webhook_url when submitting a generation. AI Maraya sends a terminal event after the job completes, fails or is cancelled, retries temporary delivery failures, and signs the exact request body using your separately issued webhook secret.

x-aimaraya-signature: v1=HMAC_SHA256(webhook_secret, timestamp + "." + raw_body)
x-aimaraya-timestamp: UNIX_SECONDS
x-aimaraya-delivery: UNIQUE_DELIVERY_ID
Looking for tutorials instead of raw endpoints? Check the guides.