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 -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=720pconst 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
| category | required | video · image · audio |
| model | required | A model id from GET /v1/models (e.g. vseed25, vk30, as_seedream5, as_nano_pro) |
| prompt | required | What to generate. Reference uploaded files with @image1, @video1, @audio1 (see below). |
| duration | video | e.g. 5s … 30s (per-model range in /v1/models). Ignored for video edits — see below. |
| aspect_ratio | video/image | 16:9 · 9:16 · 1:1 · 4:3 · 3:4 · 21:9 · adaptive |
| resolution | video/image | 480p · 720p · 1080p (per model) |
| mode | optional | text (default) · refs (use reference files) · frames (first/last frame) |
| files | optional | Reference files, repeat the field per file (images, videos, audio). Up to ~4 MB per request — use chunked uploads for larger. |
| uploaded_files | optional | JSON list from chunked uploads: [{"uploadId":"…","position":0}] |
| image_url / image_urls | optional | Reference images by public HTTPS link instead of uploading: one URL, or a JSON list ["https://…","https://…"] |
| video_url / video_urls | optional | Reference videos by public HTTPS link (one URL or a JSON list) |
| audio_url / audio_urls | optional | Reference audio by public HTTPS link (one URL or a JSON list) |
| slot_kinds | frames mode | JSON list aligned to files, e.g. ["start","end"] for first/last frame |
| generate_audio | optional | true (default) / false — native audio on supported video models |
| webhook_url | optional | Public HTTPS URL to receive a signed completion event |
| parameters | optional | JSON 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 -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.jpgcurl -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 -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.jpgLong 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).
# 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
{
"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
/v1/generationsQueue an image, video or audio generation (multipart form)./v1/generations/{id}Status, progress and output URL of a job./v1/generationsList recent generations created by this API key./v1/generations/{id}/cancelCancel a queued or running generation./v1/modelsAvailable models, durations, resolutions and reference limits./v1/uploads/initStart a chunked upload for reference files over 4 MB./v1/uploads/{id}/chunk?index=NUpload one chunk (raw bytes)./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-timestamp: UNIX_SECONDS
x-aimaraya-delivery: UNIQUE_DELIVERY_ID