● nureta
Docs

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.

ModeInput
Text-to-videoA text prompt only. No images are needed.
Image-to-videoUp 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-shot explicit flag
  • 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
SituationResult
A revoked key401 AuthenticationError
Your balance cannot cover a generation403 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

  1. Create an API key on the API keys page.
  2. Create a task. The response is 201 with the task view.
  3. Poll the task until status is succeeded or failed. Or pass callback_url at 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:

KindCreate / listRetrieve / 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
OperationRequestResponse
CreatePOST on the create / list route201 with the task view.
RetrieveGET …/:idThe task view. A task id of another kind (for example a video id on the image route) returns 404 NotFound.
ListGET on the create / list route, with ?limit= (1–100, default 20) and ?offset={"items": [task, ...], "total": N}. Your tasks of one kind, newest first.
DeleteDELETE …/:id200 {}. 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.

  1. Ask for a signed upload URL.
  2. PUT the image bytes straight to storage.
  3. Pass the returned assetUrl as an image_url.url in 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

FieldNotes
fileNameOptional. Used to derive the stored object name, and the content type when contentType is not sent.
contentTypeOne 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

FieldNotes
uploadUrlThe signed URL for the PUT. It is short-lived, so request a fresh one per file.
assetUrlThe 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.
contentTypeThe type signed into uploadUrl. Send exactly this Content-Type on the PUT.
previewUrlA 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.

OperationRequestResponse
GeneratePOST /api/v3/library/generations202 with {id, kind, status, price_usd}. Charges $0.10 (2 images at the seahorse-image price).
PollGET /api/v3/library/generations/:idThe generation.
RecentGET /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.
SavePOST /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.
ListGET /api/v3/library/items with ?kind=character or background, ?limit= (1–100, default 20) and ?cursor={"items": [item, ...], "next_cursor": "…" or null}. Newest first.
RetrieveGET /api/v3/library/items/:idThe item.
RenamePATCH /api/v3/library/items/:id with {"name"?, "note"?}The item. Only the name and the note change.
DeleteDELETE /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

FieldRequiredNotes
kindyescharacter or background.
specyesThe description, as fields. Another field returns 400 InvalidParameter.
spec.sexcharacterfemale or male.
spec.age_groupnoCharacter: 20s (default), 30s, 40s or 50plus.
spec.stylenorealistic (default), anime, 3d, illustration, or your own words (up to 60 characters).
spec.appearance, spec.hair, spec.body, spec.outfitnoCharacter: up to 300 characters each.
spec.locationbackgroundThe place, up to 200 characters.
spec.time_of_daynoBackground: day or night.
spec.moodnoBackground: up to 300 characters. People may appear in a background.
promptnoMore 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_zonenoAn 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.

FieldRequiredNotes
fromfor a candidateA candidate ref (gen_...#0) of one of your own generations.
kindfor an uploadbackground. A character cannot be uploaded (422 library_upload_character_refused).
image_urlfor an uploadAn 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.
consentfor an uploadtrue: 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.
nameno1–60 characters. Without a name, the API gives the item a default name. Rename it later with PATCH.
notenoUp to 500 characters.
time_zonenoAn 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

FieldMeaning
idgen_...
kindcharacter or background.
statuschecking (the description check), then generating, then done, rejected or failed. pending only on a pending_id (see below).
reject_codelibrary_prompt_rejected or library_generation_failed, else null.
categoriesWith 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, promptYour description.
price_usd, refundedThe charge, and whether it was refunded.
created_at, expires_atUnsaved 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

FieldMeaning
idchr_... (a character) or bg_... (a background). Use it as a library_item in a video.
reflibrary:chr_... or library:bg_.... Use it in the storyboard edit fields that take an image.
kindcharacter or background.
sourcegenerated (a candidate that the library drew) or upload (a background that you uploaded).
name, noteYour 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, descriptionFrom the generation. They never change. To change the look, generate again and save a new item.
statusready.
thumb_urlA signed link to a small preview, valid for 15 minutes. Fetch the item again for a new one.
width, heightThe image size.
created_at, updated_atISO 8601 times.
expires_at1 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

LimitValue
Saved items200 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.
Generations100 per day, and 2 running at a time.
Saves20 uploaded backgrounds per day. Saving a candidate does not count toward this limit.
Upload checks40 checks of uploaded images per day, refused ones included, and one upload save at a time.
Requests10 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.

CodeHTTPWhen
library_item_not_found404The item does not exist, was deleted, or belongs to another account (the same answer).
library_generation_not_found404The generation does not exist or belongs to another account.
library_candidate_unavailable404Save: the candidate does not exist, expired, or belongs to another account.
library_prompt_rejected422A name or note failed the word filter (categories). A refused description is a generation with status: "rejected".
library_kind_mismatch422A background where a character goes, or the reverse.
library_ref_unsupported422A library item with a direct Seedance model.
library_bad_source422Save: 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_refused422Save: an upload with kind: "character". Only backgrounds can be uploaded.
library_consent_required422Save: an upload without consent: true.
library_upload_rejected422Save: the uploaded image may show a minor. Nothing is saved.
library_upload_unchecked422Save: the check of the uploaded image failed, so nothing is saved. Retry later.
library_upload_too_large422Save: the uploaded image is larger than 15 MB or 40 megapixels.
library_upload_unreadable422Save: the image could not be downloaded, is not a JPEG, PNG or WebP, or is animated.
library_disabled422The library is not enabled.
library_limit_reached409A limit was reached. See Limits.
library_busy409 / 503409: another change to your library is running, so retry. 503: too many generations are queued.
library_upload_limit429Save: 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_cursor400A cursor that this API did not return.
LibraryUnavailable503The library is not available right now. Retry later. On generate, a pending_id means you were charged: poll it (see The generation).

Uploaded references and generated outputs are stored privately.

MediaLifetimeNotes
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 uploadDeleted 3 days after the video that uses it is delivered, or 72 hours after upload if no delivered video uses itIt 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 previewsExpire after 14 daysThey are working files.
Library thumb_url (items and candidates)Valid for 15 minutesFetch 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 imagesKept 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

RuleBehavior
GuaranteeAt least once. Receivers should deduplicate by id.
RetriesA failed delivery is retried at least 30 seconds after the previous attempt, up to 5 attempts in total.
DeletionDeleting the task stops further deliveries.
SuccessThe endpoint returns any 2xx status.
Timeout5 seconds.
RedirectsNot followed.

Events

X-Nureta-EventWhenRetried
generation.task.completedThe terminal event, as described above. For an edit-mode task, it still fires after you render.Yes
generation.task.plannedAn 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.completedA 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 provided

Every 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:

  1. Compute an HMAC-SHA256 hex digest over X-Nureta-Timestamp + "." + rawBody, using the secret.
  2. Remove the sha256= prefix from X-Nureta-Signature (the same value as X-Tokenstore-Signature).
  3. 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:

RouteCap
Upload presigning30 requests/minute per account
Task deletion10 per minute per account
Re-render quote, start and re-stitch12 requests per minute per account
Library generation10 requests per minute per account
Library save20 requests per minute per account

Errors

Errors return a JSON body {"error": {"code": "...", "message": "..."}}:

CodeHTTPWhen
AuthenticationError401The API key is missing, invalid or revoked.
InvalidParameter400A 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.
InvalidParameter413The request body is larger than 1 MB.
InvalidParameter415The request body uses an unsupported charset or content encoding.
AccountOverdueError403Your 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.
MediaNotOwned403The request uses a media URL that another account uploaded or generated, or the link of a library image (use the item id).
media_expired410The 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.
ModelNotFound404Unknown model id.
NotFound404Unknown task id, a task of another kind, or an unknown /api/v3 route.
Conflict409The 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_failed422The request was rejected at once for its media or prompt, for example a storyboard edit. See Task errors.
RateLimitExceeded429A rate limit was hit. See Rate limits.
InternalServiceError500An 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.codeMeaning
input_media_minor_rejectedA reference photo appears to show someone under 18.
input_media_too_largeA reference photo is too large to check (max 40 MB and 100 megapixels).
input_media_over_limitA reference photo is too large to use (upload one under 10 MB).
input_media_unreadableA reference photo could not be read.
prompt_policy_rejectedThe content policy blocked the prompt.
content_filter_rejectedThe content filter blocked the output.
quality_check_failedThe scene could not be rendered cleanly.
InternalServiceErrorGeneration failed ("Generation failed. You have been refunded.").
library_item_not_foundA library item of the task was deleted before the task started.
DraftExpiredAn 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/uploads accepts only image, video and audio types a task can use (see Upload a reference image). Without contentType, the type comes from the fileName extension; with neither, the request returns 400 InvalidParameter. The response has a new contentType field: send it on the PUT.
  • 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 returns 503 start_in_progress; retry it with the same Idempotency-Key.