Overview
Nureta Developer API docs for adult video, image and script generation: base URL, API keys, rate limits, uploads, signed webhooks and errors.
Nureta is the No.1 unfiltered NSFW AI video platform, trusted by 50,000+ users. The Nureta Developer API is the same engine, open to your code. It turns prompts and reference media into video, images and scripts.
Every endpoint shares one authentication model, one task lifecycle (create → poll → terminal), and one set of callbacks and errors. This page documents them. The details for each medium are under Video, Image and Script.
Pricing
API pricing is per second of video. For example, the Seahorse model seahorse-480p (Nureta's own pipeline with NSFW enhancement) is about $0.19/s. The direct Seedance 2.0 models start at $0.0885/s (single clips, no NSFW enhancement). You pay as you go from a prepaid USDC balance. Looking for the app? Consumer pricing is $1.29 per 5-second 480p video ($2.58 at 720p, $3.87 at 1080p), and the first video is free (a 5-second 480p clip with Google sign-in, with a small watermark). The Nureta app and the Nureta Developer API both offer 480p, 720p and 1080p. See nureta.ai/en/pricing.
What the API does
Video works in two modes. With the seahorse-* models, both run up to 180 seconds (3 minutes) as stitched multi-segment scenes. The direct Seedance models (seedance2.0-fast, seedance2.0-pro) render single clips of 5, 8, 10, 12 or 15 seconds, without scenes.
| Mode | Input |
|---|---|
| Text-to-video | A text prompt only. No images are needed. |
| Image-to-video | Up to 9 image_url items in total (reference images and pinned first/last frames count together). Use reference images or frames, not both. |
Video runs at 480p–1080p and 5s–180s. The API is built for automated, scalable production with granular control. You can:
- generate in batches from your own pipeline
- supply your own storyboard
segments[], with a per-shotexplicitflag - pin first/last frames
- turn on auto chaining for smoother joins between the shots of a long video (the render takes longer, the price stays the same)
- review and edit the draft storyboard before rendering (beats, durations, background plates, shared world/wardrobe/character)
- attach up to 9 images (reference images and pinned frames count together) and optional audio
- generate characters and backgrounds, save them in your Library, and use them in any video by ID
- receive signed webhooks
The Nureta consumer app (nureta.ai) runs on this same API. The Nureta app and the Nureta Developer API both offer 480p, 720p and 1080p.
Content policy
The API is built for adult content: explicit adult scenes that mainstream APIs refuse. Nureta's content policy governs its use. It allows adults only, with no minors, no real people without consent and no public figures. The API rejects reference images that look like a public figure automatically.
Base URL
All endpoints are served from https://developer.nureta.ai. The examples use a $NURETA_URL shell variable for it. Set it once:
export NURETA_URL="https://developer.nureta.ai"SDKs
SDKs are available on request. Every example works with plain curl. There is no proprietary transport, just REST.
Authentication
Every request except the model lists (GET /api/v3/models, /api/v3/images/models, /api/v3/scripts/models) authenticates with an API key. Create one on the API keys page. Pass it as a bearer token:
Authorization: Bearer sk-...
Content-Type: application/json| Situation | Result |
|---|---|
| A revoked key | 401 AuthenticationError |
| Your balance cannot cover a generation | 403 AccountOverdueError |
You can revoke a key on the API keys page. When you request deletion of your account, every key of the account is revoked at once. Undoing the deletion does not restore the keys. Create new ones.
Balance
Generations are billed to your balance at the posted model prices. GET /api/v3/account returns your current balance:
curl -s "$NURETA_URL/api/v3/account" -H "Authorization: Bearer sk-..."
# => {"balance_usd": 12.5}Quick start
- Create an API key on the API keys page.
- Create a task. The response is
201with the task view. - Poll the task until
statusissucceededorfailed. Or passcallback_urlat create time to receive a signed webhook instead.
# 1. Create a video task.
curl -s -X POST "$NURETA_URL/api/v3/contents/generations/tasks" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"model": "seahorse-480p", "content": [{"type": "text", "text": "your prompt"}], "duration": 5}'
# => HTTP 201 with the task view: {"id": "cgt-...", "status": ..., ...}
# 2. Poll it.
curl -s "$NURETA_URL/api/v3/contents/generations/tasks/cgt-..." \
-H "Authorization: Bearer sk-..."
# => {"id": "cgt-...", "status": "succeeded", "content": {"video_url": "..."}, "usage": {...}}Image and script tasks work the same way on their own routes.
Tasks
Video, image and script tasks follow the same pattern. Each kind has its own routes:
| Kind | Create / list | Retrieve / delete |
|---|---|---|
| Video | /api/v3/contents/generations/tasks | /api/v3/contents/generations/tasks/:id |
| Image | /api/v3/images/generations/tasks | /api/v3/images/generations/tasks/:id |
| Script | /api/v3/contents/scripts | /api/v3/contents/scripts/:id |
| Operation | Request | Response |
|---|---|---|
| Create | POST on the create / list route | 201 with the task view. |
| Retrieve | GET …/:id | The task view. A task id of another kind (for example a video id on the image route) returns 404 NotFound. |
| List | GET on the create / list route, with ?limit= (1–100, default 20) and ?offset= | {"items": [task, ...], "total": N}. Your tasks of one kind, newest first. |
| Delete | DELETE …/:id | 200 {}. Only a finished task (succeeded, failed or cancelled). |
The task view
Create, retrieve and list return the same shape: the task view. It includes your inputs: prompt, for video and image the reference media as [{url, type}], and for video the pinned first_frame and last_frame when they are set. Video list items do not carry re-render runs. GET the task for those.
Delete a task
A task that is not finished returns 409 Conflict. Cancel an edit-mode draft first (POST …/cancel), or wait until a running task finishes. Deleting a video stops any re-render of it that is still running and refunds that run. If the run cannot be stopped, the delete returns 409 Conflict. Try again shortly.
You can delete at most 10 tasks per minute per account. More returns 429 RateLimitExceeded.
Upload a reference image
POST /api/v3/contents/uploads gives you a short-lived signed URL for a reference image. Use it when you do not already host the image at a public URL. It authenticates with your API key. Uploading is free: you are billed only when you create a generation. Your balance must be above $0 to upload; otherwise the request returns 403 AccountOverdueError.
- Ask for a signed upload URL.
PUTthe image bytes straight to storage.- Pass the returned
assetUrlas animage_url.urlin a generation task.
# 1. Ask for a signed upload URL.
UPLOAD=$(curl -s -X POST "$NURETA_URL/api/v3/contents/uploads" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"fileName": "ref.jpg", "contentType": "image/jpeg"}')
# => {"uploadUrl": "https://...signed...", "assetUrl": "https://.../ref.jpg",
# "contentType": "image/jpeg", "previewUrl": "https://...signed.../ref.jpg"}
# 2. Upload the bytes to the signed URL (same Content-Type, no auth header).
UPLOAD_URL=$(echo "$UPLOAD" | jq -r .uploadUrl)
ASSET_URL=$(echo "$UPLOAD" | jq -r .assetUrl)
curl -s -X PUT "$UPLOAD_URL" -H "Content-Type: image/jpeg" --data-binary @ref.jpg
# 3. Reference ASSET_URL as an image_url when you create a generation task:
# {"type": "image_url", "image_url": {"url": "'"$ASSET_URL"'"}}Request fields
| Field | Notes |
|---|---|
fileName | Optional. Used to derive the stored object name, and the content type when contentType is not sent. |
contentType | One of image/jpeg, image/png, image/webp, image/heic, image/heif, image/heic-sequence, image/heif-sequence, video/mp4, video/quicktime, audio/mpeg, audio/mp3, audio/wav, audio/x-wav, audio/mp4, audio/x-m4a. Another type returns 400 InvalidParameter. Without it, the type comes from the fileName extension (.jpg, .jpeg, .png, .webp, .heic, .heif, .mp4, .mov, .mp3, .wav, .m4a); with neither, the request returns 400 InvalidParameter. |
Response fields
| Field | Notes |
|---|---|
uploadUrl | The signed URL for the PUT. It is short-lived, so request a fresh one per file. |
assetUrl | The value to pass as task input. Uploaded files are deleted 3 days after the video that uses them is delivered, or 72 hours after upload if no delivered video uses them. Upload again to use a file in a later task. |
contentType | The type signed into uploadUrl. Send exactly this Content-Type on the PUT. |
previewUrl | A signed link to view the uploaded file. If signing is briefly unavailable, it equals assetUrl and cannot be opened. The upload itself still works. |
Pass assetUrl, not previewUrl, as the task input.
Library
Save characters and backgrounds, and use them in any video by their ID. Describe a person or a place, the API draws 2 images, and you save the one you want. You can also save a background that you upload. Your saved items belong to your account only: no other account can see or use them. A saved item is kept for 1 year after you save it, then it is deleted.
A character is always one that the library draws itself. You cannot save an uploaded photo of a person, or the result of an image task: an uploaded photo of a person is used for one task only. Every character is an original adult, 20 or older; the description is checked before anything is drawn. An uploaded background is checked before it is saved.
| Operation | Request | Response |
|---|---|---|
| Generate | POST /api/v3/library/generations | 202 with {id, kind, status, price_usd}. Charges $0.10 (2 images at the seahorse-image price). |
| Poll | GET /api/v3/library/generations/:id | The generation. |
| Recent | GET /api/v3/library/generations with ?kind=, ?limit= (1–100, default 20) and ?cursor= | {"items": [generation, ...], "next_cursor": "…" or null}. The last 14 days, newest first. |
| Save | POST /api/v3/library/items with a candidate or an uploaded background (see Save fields) | 201 with the item. Free. The same candidate saved again returns 200 with the same item. |
| List | GET /api/v3/library/items with ?kind=character or background, ?limit= (1–100, default 20) and ?cursor= | {"items": [item, ...], "next_cursor": "…" or null}. Newest first. |
| Retrieve | GET /api/v3/library/items/:id | The item. |
| Rename | PATCH /api/v3/library/items/:id with {"name"?, "note"?} | The item. Only the name and the note change. |
| Delete | DELETE /api/v3/library/items/:id | {"id", "status": "deleted", "files_deleted"}. See Delete an item. |
You can also generate and manage items on the Library page of the console.
Generate, save and use
# 1. Generate 2 candidate images ($0.10).
GEN_ID=$(curl -s -X POST "$NURETA_URL/api/v3/library/generations" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"kind": "character",
"spec": {"sex": "female", "age_group": "30s", "style": "anime",
"hair": "short red hair", "outfit": "green raincoat"},
"prompt": "freckles, a calm smile",
"time_zone": "Asia/Tokyo"}' | jq -r .id)
# 2. Poll until status is done, rejected or failed (usually under a minute).
curl -s "$NURETA_URL/api/v3/library/generations/$GEN_ID" -H "Authorization: Bearer sk-..."
# => {"id": "gen_...", "status": "done",
# "candidates": [{"ref": "gen_...#0", "thumb_url": "https://..."}, ...], ...}
# 3. Save the candidate you want. The name is optional: without one, the API names it.
curl -s -X POST "$NURETA_URL/api/v3/library/items" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"name": "Mira", "from": "'"$GEN_ID"'#0"}'
# => 201 {"id": "chr_...", "ref": "library:chr_...", "kind": "character", "source": "generated", "name": "Mira", ...}
# 4. Use it in a video.
curl -s -X POST "$NURETA_URL/api/v3/contents/generations/tasks" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"model": "seahorse-480p", "content": [
{"type": "text", "text": "She waits by the window, rain outside."},
{"type": "library_item", "library_item": {"id": "chr_..."}}
]}'Generation fields
| Field | Required | Notes |
|---|---|---|
kind | yes | character or background. |
spec | yes | The description, as fields. Another field returns 400 InvalidParameter. |
spec.sex | character | female or male. |
spec.age_group | no | Character: 20s (default), 30s, 40s or 50plus. |
spec.style | no | realistic (default), anime, 3d, illustration, or your own words (up to 60 characters). |
spec.appearance, spec.hair, spec.body, spec.outfit | no | Character: up to 300 characters each. |
spec.location | background | The place, up to 200 characters. |
spec.time_of_day | no | Background: day or night. |
spec.mood | no | Background: up to 300 characters. People may appear in a background. |
prompt | no | More description, up to 1000 characters. It is not an image prompt: the API puts the description into a fixed template (one clothed adult, full body, plain background, the chosen style). |
time_zone | no | An IANA time zone, for example Asia/Tokyo. The daily limits reset at midnight in this zone (default UTC). The first zone you send is kept; a different one is taken at most once per 24 hours. |
Save fields
Send exactly one of from or image_url. A body with both, or with neither, returns 422 library_bad_source.
| Field | Required | Notes |
|---|---|---|
from | for a candidate | A candidate ref (gen_...#0) of one of your own generations. |
kind | for an upload | background. A character cannot be uploaded (422 library_upload_character_refused). |
image_url | for an upload | An https URL on a public host of a still JPEG, PNG or WebP image of at most 15 MB and 40 megapixels, for example the assetUrl from Upload a reference image. The API does not follow a redirect. A URL in the API's storage must be the assetUrl of your own upload: another user's file is 403 MediaNotOwned, and any other file there (an output, a frame) is 422 library_bad_source. The image is saved as a JPEG of at most 4096 px, with EXIF and GPS data removed. |
consent | for an upload | true: you have the right to use the image, and anyone who appears in it is an adult who agreed to be shown. Anything else returns 422 library_consent_required. |
name | no | 1–60 characters. Without a name, the API gives the item a default name. Rename it later with PATCH. |
note | no | Up to 500 characters. |
time_zone | no | An IANA time zone for the daily save limit (see Limits). |
# Upload a background, then save it.
curl -s -X POST "$NURETA_URL/api/v3/library/items" \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"kind": "background", "image_url": "https://.../room.jpg", "consent": true, "name": "Rainy loft"}'
# => 201 {"id": "bg_...", "ref": "library:bg_...", "kind": "background", "source": "upload", "name": "Rainy loft", ...}The API downloads the image once and checks it before it saves it. An upload save answers within about 75 seconds: wait at least 90 seconds before you give up on the call. An image over 15 MB or 40 megapixels returns 422 library_upload_too_large. An image that cannot be downloaded, is not a JPEG, PNG or WebP, or is animated, returns 422 library_upload_unreadable. When the image may show a minor, the save returns 422 library_upload_rejected. When the check cannot decide, it returns 422 library_upload_unchecked: try again later.
The generation
| Field | Meaning |
|---|---|
id | gen_... |
kind | character or background. |
status | checking (the description check), then generating, then done, rejected or failed. pending only on a pending_id (see below). |
reject_code | library_prompt_rejected or library_generation_failed, else null. |
categories | With library_prompt_rejected: minors, public_figure, copyrighted_character, or library_check_unavailable (the check could not decide, so nothing was drawn). |
candidates[] | {ref, thumb_url} per image. Save one by its ref. The list is empty after 14 days. |
spec, prompt | Your description. |
price_usd, refunded | The charge, and whether it was refunded. |
created_at, expires_at | Unsaved candidates are deleted at expires_at (14 days). |
When the library does not answer. Generate can return 503 LibraryUnavailable with a pending_id (lgc_...) after you were charged: the generation may have started. Poll GET /api/v3/library/generations/{pending_id}. It shows status: "pending" until the API knows, then the generation itself. If it never starts, it is refunded within an hour.
Pricing and refunds. A generation costs $0.10 and is charged when you create it. When it ends rejected or failed (no image), the $0.10 is refunded automatically, also if you never poll it. A generation that ends done with 1 image (a draw still failed after its retries) is not refunded. Saving is free.
The item
| Field | Meaning |
|---|---|
id | chr_... (a character) or bg_... (a background). Use it as a library_item in a video. |
ref | library:chr_... or library:bg_.... Use it in the storyboard edit fields that take an image. |
kind | character or background. |
source | generated (a candidate that the library drew) or upload (a background that you uploaded). |
name, note | Your name (1–60 characters; a default name when you saved without one) and note (up to 500). Both pass a word filter: use a made-up name, not a real person's full name. |
sex, style, description | From the generation. They never change. To change the look, generate again and save a new item. |
status | ready. |
thumb_url | A signed link to a small preview, valid for 15 minutes. Fetch the item again for a new one. |
width, height | The image size. |
created_at, updated_at | ISO 8601 times. |
expires_at | 1 year after created_at. The item is deleted then, as if you deleted it (see Delete an item). |
Use an item in a video
Send {"type": "library_item", "library_item": {"id": "chr_..."}} in the content of a video task. A character is a reference image of a person. A background is a reference image of the place. Storyboard edits take library:bg_... and library:chr_... in their image fields. See Library items in a video.
The video is checked like any video with an uploaded image. A library item is not available for first or last frames, for image tasks, or with the direct Seedance models.
Delete an item
The item is deleted at once. A new task or storyboard edit that uses it, and the render of a draft that holds it, fails with 404 library_item_not_found. A task that is already running finishes. Its image and preview are deleted about an hour later, when no running task uses them.
Limits
| Limit | Value |
|---|---|
| Saved items | 200 characters and 200 backgrounds. A generation is refused when that kind is full, before anything is drawn (and the charge is refunded). An upload is refused when your backgrounds are full. |
| Generations | 100 per day, and 2 running at a time. |
| Saves | 20 uploaded backgrounds per day. Saving a candidate does not count toward this limit. |
| Upload checks | 40 checks of uploaded images per day, refused ones included, and one upload save at a time. |
| Requests | 10 generations and 20 saves per minute per account. See Rate limits. |
A day ends at midnight in your time_zone. A limit returns 409 library_limit_reached with limit (max_characters, max_backgrounds, daily_generations, daily_saves or running_generations) and, for a daily limit, resets_at. The upload checks return 429 library_upload_limit with limit (daily_upload_checks with resets_at, or one_at_a_time).
Library errors
Errors use the error format. A library error can also carry limit, resets_at and categories.
| Code | HTTP | When |
|---|---|---|
library_item_not_found | 404 | The item does not exist, was deleted, or belongs to another account (the same answer). |
library_generation_not_found | 404 | The generation does not exist or belongs to another account. |
library_candidate_unavailable | 404 | Save: the candidate does not exist, expired, or belongs to another account. |
library_prompt_rejected | 422 | A name or note failed the word filter (categories). A refused description is a generation with status: "rejected". |
library_kind_mismatch | 422 | A background where a character goes, or the reverse. |
library_ref_unsupported | 422 | A library item with a direct Seedance model. |
library_bad_source | 422 | Save: send exactly one of from or image_url. image_url must be an https URL on a public host that does not redirect, with no spaces or control characters, and a URL in the API's storage must be an upload's assetUrl. |
library_upload_character_refused | 422 | Save: an upload with kind: "character". Only backgrounds can be uploaded. |
library_consent_required | 422 | Save: an upload without consent: true. |
library_upload_rejected | 422 | Save: the uploaded image may show a minor. Nothing is saved. |
library_upload_unchecked | 422 | Save: the check of the uploaded image failed, so nothing is saved. Retry later. |
library_upload_too_large | 422 | Save: the uploaded image is larger than 15 MB or 40 megapixels. |
library_upload_unreadable | 422 | Save: the image could not be downloaded, is not a JPEG, PNG or WebP, or is animated. |
library_disabled | 422 | The library is not enabled. |
library_limit_reached | 409 | A limit was reached. See Limits. |
library_busy | 409 / 503 | 409: another change to your library is running, so retry. 503: too many generations are queued. |
library_upload_limit | 429 | Save: today's upload checks are used up (limit: "daily_upload_checks", retry after resets_at), or another upload save is still running (limit: "one_at_a_time"). |
invalid_cursor | 400 | A cursor that this API did not return. |
LibraryUnavailable | 503 | The library is not available right now. Retry later. On generate, a pending_id means you were charged: poll it (see The generation). |
Media links are private
Uploaded references and generated outputs are stored privately.
| Media | Lifetime | Notes |
|---|---|---|
Signed links in task responses and callbacks (video_url, image_url, storyboard frames and previews) | Valid for 7 days. The service reuses one signed link for up to 24 hours, so a link you receive can have about 6 days left. | Fetch the task again for a newer link. A re-fetch within 24 hours can return the same link. Do not store links long-term. |
assetUrl from an upload | Deleted 3 days after the video that uses it is delivered, or 72 hours after upload if no delivered video uses it | It is an identifier, not a public link. It cannot be opened directly. Upload again to use a file in a later task. A request that names a deleted upload returns 410 media_expired. |
| Storyboard frame previews | Expire after 14 days | They are working files. |
Library thumb_url (items and candidates) | Valid for 15 minutes | Fetch the item or generation again for a new link. A library image link is never task input: use the item id. |
| Final videos and generated images | Kept until you delete the task or your account |
Media can only be referenced by the account that uploaded or generated it. Using another account's URL returns 403 MediaNotOwned.
Callback webhook
When callback_url is provided on task creation, Nureta Developer POSTs the terminal task view to that URL. This happens when the task becomes succeeded or failed, or when you cancel an edit-mode draft (the body then has status: "cancelled").
The JSON body of generation.task.completed and generation.task.planned is the same task view that retrieving the task returns.
Delivery
| Rule | Behavior |
|---|---|
| Guarantee | At least once. Receivers should deduplicate by id. |
| Retries | A failed delivery is retried at least 30 seconds after the previous attempt, up to 5 attempts in total. |
| Deletion | Deleting the task stops further deliveries. |
| Success | The endpoint returns any 2xx status. |
| Timeout | 5 seconds. |
| Redirects | Not followed. |
Events
X-Nureta-Event | When | Retried |
|---|---|---|
generation.task.completed | The terminal event, as described above. For an edit-mode task, it still fires after you render. | Yes |
generation.task.planned | An edit-mode task with a callback_url fires it once, when the storyboard draft is ready. The body carries status: "planned" and the scene. | No. It is best-effort and separate from the terminal event. |
generation.task.rerender.completed | A re-render run of the video finished. The body is {event, task_id, rerender, task}, not the task view. See Fix part of a video. | Yes |
Headers
POST /webhooks/nureta-developer HTTP/1.1
Content-Type: application/json
User-Agent: tokenstore-webhook/1.0
X-Nureta-Event: generation.task.completed
X-Nureta-Task-Id: cgt-...
X-Nureta-Delivery: 00000000-0000-0000-0000-000000000000
X-Nureta-Timestamp: 1781220120
X-Nureta-Signature: sha256=<hex-hmac> # only when callback_secret was providedEvery X-Nureta-* header is also sent under its legacy X-Tokenstore-* name with the identical value (for example X-Tokenstore-Event, X-Tokenstore-Signature). The User-Agent stays tokenstore-webhook/1.0. This keeps existing receivers working. Use either name. New integrations should read X-Nureta-*.
Signature verification
When callback_secret was provided:
- Compute an HMAC-SHA256 hex digest over
X-Nureta-Timestamp + "." + rawBody, using the secret. - Remove the
sha256=prefix fromX-Nureta-Signature(the same value asX-Tokenstore-Signature). - Compare the rest with your digest in constant time.
Safety rules for callback_url
- It must use HTTPS.
- It must not include credentials.
- It must be 2048 characters or fewer.
- It must not target localhost, or private, loopback, link-local, multicast or otherwise reserved IP ranges.
An IP literal or a localhost name is rejected with 400 InvalidParameter. A hostname is checked at each delivery. If it resolves to such an address, the task is still accepted, but no delivery to it succeeds.
Rate limits
Every request made with an API key counts toward that key's rate limit. This includes task creation, status polling, storyboard edits and uploads. New keys default to 10 requests/second. You can adjust each key between 1 and 100 req/s on the API keys page.
Exceeding the limit returns 429 RateLimitExceeded with standard RateLimit headers. Back off briefly and retry.
Some routes have an additional fixed cap:
| Route | Cap |
|---|---|
| Upload presigning | 30 requests/minute per account |
| Task deletion | 10 per minute per account |
| Re-render quote, start and re-stitch | 12 requests per minute per account |
| Library generation | 10 requests per minute per account |
| Library save | 20 requests per minute per account |
Errors
Errors return a JSON body {"error": {"code": "...", "message": "..."}}:
| Code | HTTP | When |
|---|---|---|
AuthenticationError | 401 | The API key is missing, invalid or revoked. |
InvalidParameter | 400 | A request field is missing or invalid (e.g. "duration must be one of 5, 8, 10, 12, 15 seconds (single clip) or an integer from 16 to 180 seconds (multi-segment scene)"), or the body is not valid JSON. |
InvalidParameter | 413 | The request body is larger than 1 MB. |
InvalidParameter | 415 | The request body uses an unsupported charset or content encoding. |
AccountOverdueError | 403 | Your balance cannot cover the task. An edit-mode render also returns it when the final storyboard costs more than your balance can cover. An upload returns it when your balance is $0 or less. |
MediaNotOwned | 403 | The request uses a media URL that another account uploaded or generated, or the link of a library image (use the item id). |
media_expired | 410 | The request uses an upload of yours that was deleted (3 days after the video that used it was delivered, or 72 hours after upload). Upload the file again and use its new assetUrl. |
ModelNotFound | 404 | Unknown model id. |
NotFound | 404 | Unknown task id, a task of another kind, or an unknown /api/v3 route. |
Conflict | 409 | The task is in the wrong state. Examples: a delete of an unfinished task, or a render or cancel of a draft that is not planned. |
input_media_*, prompt_policy_rejected, content_filter_rejected, quality_check_failed | 422 | The request was rejected at once for its media or prompt, for example a storyboard edit. See Task errors. |
RateLimitExceeded | 429 | A rate limit was hit. See Rate limits. |
InternalServiceError | 500 | An unexpected server error. |
The re-render routes have their own codes (IdempotencyKeyReused, rerender_running). See Fix part of a video. The library has its own codes (library_*). See Library errors.
Task errors
A task that fails has status: "failed" and an error object. Its price is refunded automatically. error.code is one of:
error.code | Meaning |
|---|---|
input_media_minor_rejected | A reference photo appears to show someone under 18. |
input_media_too_large | A reference photo is too large to check (max 40 MB and 100 megapixels). |
input_media_over_limit | A reference photo is too large to use (upload one under 10 MB). |
input_media_unreadable | A reference photo could not be read. |
prompt_policy_rejected | The content policy blocked the prompt. |
content_filter_rejected | The content filter blocked the output. |
quality_check_failed | The scene could not be rendered cleanly. |
InternalServiceError | Generation failed ("Generation failed. You have been refunded."). |
library_item_not_found | A library item of the task was deleted before the task started. |
DraftExpired | An edit-mode draft was left unrendered for 24 hours. It was refunded ("Storyboard draft expired without rendering; you have been refunded."). |
Other upstream failure codes can also appear, with the message "Generation failed. You have been refunded."
Changelog
2026-11-03
POST /api/v3/contents/uploadsaccepts only image, video and audio types a task can use (see Upload a reference image). WithoutcontentType, the type comes from thefileNameextension; with neither, the request returns400 InvalidParameter. The response has a newcontentTypefield: send it on thePUT.- An upload with a balance of $0 or less returns
403 AccountOverdueError. - The upload presign cap (30 per minute) is now per account, not per key.
- Re-render: at most one quote at a time per video and two per account (
429 quote_running). A replay of a start that is still in progress returns503 start_in_progress; retry it with the sameIdempotency-Key.