HTTP API reference
All JSON endpoints use Authorization: Bearer with a workspace API key. Set PROJECT_ID and RUN_ID to the identifiers returned by your own requests before using the examples.
Create a video
POST /api/v1/videos
Create a project and start a video from a brief. Returns immediately with a run ID. Wait poll_after_seconds, then call get_run until the run ends. Spends the team's existing credits, within the limits approved for this key or connection.
Required permission: videos:write
The creator may ask a question instead of producing a film. A completed run does not guarantee a new video: read reply and new_films. Every project can also be opened in the Ghostmotion editor.
Request body
request_idstring · requiredA unique operation ID: 1–128 letters, digits, dots, dashes, colons or underscores. Reuse the same ID and unchanged input when retrying; use a new ID for a new operation.
at least 1 character; up to 128 characters
briefstring · requiredDescribe the video, its audience and the result you want. Passed to the creator unchanged.
at least 1 character; up to 20,000 characters
referencesarray · optionalUp to 20 completed, unexpired uploads. Remote URL references are not supported. An upload can be consumed by one request; retry that request with the same ID.
up to 20 items
references[].upload_idstring · requiredThe upload_id returned by prepare_reference_upload, after uploading the file's bytes.
filesarray · optionalFiles the user attached in their assistant, as temporary https download links (ChatGPT fills this in itself). The creator downloads each at the start of the run and uses it as a reference. To send a file's bytes yourself, use prepare_reference_upload and references instead.
up to 10 items
files[].download_urlstring · requiredA temporary https link to the file's bytes.
at least 1 character; up to 4,096 characters
files[].file_idstring · requiredThe assistant's own id for the file.
up to 200 characters
files[].mime_typestring · optionalThe file's MIME type, if known.
up to 200 characters
files[].file_namestring · optionalThe file's name, if known.
up to 200 characters
castarray · optionalCharacters to cast in this video, by character_id from list_characters or create_character. Each must be ready (list_characters says so); it takes the character's latest version.
up to 10 items
cast[].character_idstring · requiredA ready character's character_id.
at least 1 character; up to 128 characters
cast[].rolestring · optionalOptional: their role in this video, for example "lead presenter".
up to 120 characters
duration_secondsnumber · optionalRequested duration in seconds, from 1 to 600.
minimum 1; maximum 600
aspect_ratiostring · optionalRequested frame shape.
16:9 · 9:16 · 1:1 · 4:5
credit_capnumber · optionalOptional spending ceiling for this run. Can lower, never raise, the key's own per-run limit. Workspace balance and the key's remaining allowance also apply.
greater than 0
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/videos" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"request_id": "launch-video-001",
"brief": "Make a 20-second launch video for a collaborative design app. Show the problem, introduce the app, and end with an invitation to try it.",
"duration_seconds": 20,
"aspect_ratio": "16:9"
}'Response · 202
{
"project_id": "project-id",
"run_id": "run-id",
"project_url": "https://app.ghostmotion.ai/p/project-id",
"status": "accepted",
"poll_after_seconds": 15
}All response fields
project_idstring · requiredThe project_id returned by creation or project listing.
at least 1 character
run_idstring · requiredThe run_id returned when a creation or edit is accepted.
at least 1 character
project_urlstring · requiredAbsolute URL.
statusstring · requiredThe run was accepted.
accepted
poll_after_secondsnumber · requiredSeconds to wait before checking the run.
Errors
unauthorized, permission_denied, not_found, invalid_request, insufficient_credits, allowance_exhausted, cap_too_low, idempotency_conflict, request_in_progress, upload_not_ready, character_not_ready, rate_limited, internal_error, temporarily_unavailable.
Edit or continue a video
POST /api/v1/projects/{project_id}/messages
Send an edit, an answer or more direction to a project's creator. Only the project's original author can direct it. Starts a new run and uses workspace credits.
Required permission: videos:write
Wait for the current run to finish, or stop it first. A retry uses the same request ID and input. A new edit needs a new request ID. Poll the newly returned run ID for the result.
Path parameters
project_idstring · requiredThe project_id returned by creation or project listing.
at least 1 character
Request body
request_idstring · requiredA unique operation ID: 1–128 letters, digits, dots, dashes, colons or underscores. Reuse the same ID and unchanged input when retrying; use a new ID for a new operation.
at least 1 character; up to 128 characters
messagestring · requiredThe edit, answer or direction to send.
at least 1 character; up to 20,000 characters
referencesarray · optionalUp to 20 completed, unexpired uploads. Remote URL references are not supported. An upload can be consumed by one request; retry that request with the same ID.
up to 20 items
references[].upload_idstring · requiredThe upload_id returned by prepare_reference_upload, after uploading the file's bytes.
filesarray · optionalFiles the user attached in their assistant, as temporary https download links (ChatGPT fills this in itself). The creator downloads each at the start of the run and uses it as a reference. To send a file's bytes yourself, use prepare_reference_upload and references instead.
up to 10 items
files[].download_urlstring · requiredA temporary https link to the file's bytes.
at least 1 character; up to 4,096 characters
files[].file_idstring · requiredThe assistant's own id for the file.
up to 200 characters
files[].mime_typestring · optionalThe file's MIME type, if known.
up to 200 characters
files[].file_namestring · optionalThe file's name, if known.
up to 200 characters
castarray · optionalCharacters to cast in this video, by character_id from list_characters or create_character. Each must be ready (list_characters says so); it takes the character's latest version.
up to 10 items
cast[].character_idstring · requiredA ready character's character_id.
at least 1 character; up to 128 characters
cast[].rolestring · optionalOptional: their role in this video, for example "lead presenter".
up to 120 characters
credit_capnumber · optionalOptional spending ceiling for this run. Can lower, never raise, the key's own per-run limit. Workspace balance and the key's remaining allowance also apply.
greater than 0
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/projects/$PROJECT_ID/messages" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"request_id": "launch-edit-001",
"message": "Make the opening faster and add captions."
}'Response · 202
{
"project_id": "project-id",
"run_id": "edit-run-id",
"project_url": "https://app.ghostmotion.ai/p/project-id",
"status": "accepted",
"poll_after_seconds": 15
}All response fields
project_idstring · requiredThe project_id returned by creation or project listing.
at least 1 character
run_idstring · requiredThe run_id returned when a creation or edit is accepted.
at least 1 character
project_urlstring · requiredAbsolute URL.
statusstring · requiredThe run was accepted.
accepted
poll_after_secondsnumber · requiredSeconds to wait before checking the run.
Errors
unauthorized, permission_denied, not_found, invalid_request, project_busy, insufficient_credits, allowance_exhausted, cap_too_low, idempotency_conflict, request_in_progress, upload_not_ready, character_not_ready, rate_limited, internal_error, temporarily_unavailable.
Check a run and get results
GET /api/v1/runs/{run_id}
Get a run's progress, creator reply, newly published videos, project URL and credit usage. While running, wait poll_after_seconds before checking again.
Required permission: projects:read
Terminal states are completed, failed, cancelled and interrupted. new_films only contains films published by this run. Links expire after one hour; call get_run again for fresh links. Downloads recheck the issuing key's validity and project access. MCP does not automatically wake an assistant when work finishes.
Path parameters
run_idstring · requiredThe run_id returned when a creation or edit is accepted.
at least 1 character
Query parameters
No query parameters required.
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/runs/$RUN_ID" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY"Response · 200
{
"run_id": "run-id",
"project_id": "project-id",
"project_url": "https://app.ghostmotion.ai/p/project-id",
"status": "completed",
"started_at": "2026-01-01T12:00:00.000Z",
"ended_at": "2026-01-01T12:02:00.000Z",
"reply": "Your video is ready.",
"message_so_far": null,
"new_films": [
{
"version": "v001",
"variation": "a",
"url": "https://app.ghostmotion.ai/api/v1/media/example-token",
"expires_at": "2026-01-01T13:02:00.000Z"
}
],
"credits": {
"charged": 2.5,
"reserved": 0
}
}All response fields
run_idstring · requiredThe run_id returned when a creation or edit is accepted.
at least 1 character
project_idstring · requiredThe project_id returned by creation or project listing.
at least 1 character
project_urlstring · requiredAbsolute URL.
statusstring · requiredCurrent run state.
running · completed · failed · cancelled · interrupted
started_atstring · requiredISO 8601 UTC timestamp.
ended_atstring | null · requiredISO 8601 UTC timestamp.
replystring | null · requiredCreator's final reply; may ask for more information.
message_so_farstring | null · requiredCreator's current message while running.
new_filmsarray · requiredFilms published by this run; can be empty even when completed.
new_films[].versionstring · requiredRender version, for example v001.
new_films[].variationstring · requiredWhich variation of the film this version belongs to: a is the original; b, c… are alternatives started in the editor.
new_films[].urlstring · requiredAbsolute URL.
new_films[].expires_atstring · requiredISO 8601 UTC timestamp.
creditsobject · requiredcredits.chargednumber | null · requiredCredits charged on settlement; null before settlement.
credits.reservednumber · requiredCredits currently held for this run.
poll_after_secondsnumber · optionalPresent while running: wait this long before polling.
errorstring · optionalReadable failure message, when available.
error_codestring · optionalStable creator failure code, when available.
characterobject · optionalcharacter.character_idstring · requiredThe character this run is making.
character.readyboolean · requiredWhether it can be cast now.
character.versionnumber | null · requiredIts latest version; null before the first.
Errors
unauthorized, permission_denied, not_found, rate_limited, internal_error, temporarily_unavailable.
List projects
GET /api/v1/projects
List accessible workspace projects, newest first, with authorship, edit permission and the latest run.
Required permission: projects:read
Pass next_cursor unchanged as cursor to get another page. A null next_cursor means there are no more pages. Visibility does not grant edit access: check can_direct. Legacy projects without a workspace assignment are excluded until migrated.
Query parameters
limitinteger · optionalMaximum projects per page. Defaults to 20; at most 100.
minimum 1; maximum 100; default 20
cursorstring · optionalThe opaque next_cursor from the previous response.
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/projects?limit=20" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY"Response · 200
{
"projects": [
{
"project_id": "project-id",
"name": "App launch",
"project_url": "https://app.ghostmotion.ai/p/project-id",
"created_by": "Alex",
"you_created_it": true,
"can_direct": true,
"origin": "API key Launch pipeline",
"updated_at": "2026-01-01T12:02:00.000Z",
"latest_run": {
"run_id": "run-id",
"status": "completed"
}
}
],
"next_cursor": null
}All response fields
projectsarray · requiredprojects[].project_idstring · requiredThe project_id returned by creation or project listing.
at least 1 character
projects[].namestring · requiredProject name.
projects[].project_urlstring · requiredAbsolute URL.
projects[].created_bystring | null · requiredAuthor's display name.
projects[].you_created_itboolean · requiredWhether the key's owner created the project.
projects[].can_directboolean · requiredWhether the key's owner may direct the project. Write scope is still required.
projects[].originstring · requiredHow the project was created.
projects[].updated_atstring · requiredISO 8601 UTC timestamp.
projects[].latest_runobject | null · requiredprojects[].latest_run.run_idstring · requiredThe run_id returned when a creation or edit is accepted.
at least 1 character
projects[].latest_run.statusstring · requiredCurrent run state.
running · completed · failed · cancelled · interrupted
next_cursorstring | null · requiredOpaque pagination cursor; null on the last page.
Errors
unauthorized, permission_denied, not_found, invalid_request, rate_limited, internal_error, temporarily_unavailable.
List characters
GET /api/v1/characters
List the team's characters (AI characters, illustrated characters and digital twins) and whether each is ready to cast in a video.
Required permission: projects:read
Cast a ready character with cast: [{ character_id }] in create_video or send_message. A character still being made has status drafting; one whose run ended without a version has status unmade (ask for changes with send_message on its project_id). Digital twins of real people are created in Ghostmotion, where the person gives consent.
Query parameters
No query parameters required.
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/characters" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY"Response · 200
{
"characters": [
{
"character_id": "character-id",
"name": "Maya",
"kind": "ai_character",
"status": "ready",
"ready": true,
"version": 1,
"character_url": "https://app.ghostmotion.ai/characters/character-id"
}
]
}All response fields
charactersarray · requiredcharacters[].character_idstring · requiredThe character's id: cast it with this, or send_message to its workshop with it as project_id.
characters[].namestring · requiredThe character's name.
characters[].kindstring · requiredKind of character.
ai_character · illustrated · digital_twin
characters[].statusstring · requiredready: can be cast. drafting: being made. unmade: its run ended without a version. revoked: a twin whose consent was withdrawn.
ready · drafting · unmade · revoked
characters[].readyboolean · requiredWhether it can be cast now.
characters[].versionnumber | null · requiredIts latest version number; null before the first.
characters[].character_urlstring · requiredAbsolute URL.
Errors
unauthorized, permission_denied, not_found, rate_limited, internal_error, temporarily_unavailable.
Create a character
POST /api/v1/characters
Create an AI character (a photorealistic person who is not a real individual) or an illustrated character from a description and optional reference images. Returns at once with a run ID: poll get_run until it ends. When get_run's character.ready is true, cast it in videos. Uses workspace credits.
Required permission: videos:write
Describe their look, age, style and personality, and a voice if they should speak. Reference images guide the look. Follow-up changes go to send_message with the character_id as project_id. Digital twins of real people cannot be created here: they are made in Ghostmotion, where the person gives consent.
Request body
request_idstring · requiredA unique operation ID: 1–128 letters, digits, dots, dashes, colons or underscores. Reuse the same ID and unchanged input when retrying; use a new ID for a new operation.
at least 1 character; up to 128 characters
namestring · requiredThe character's name.
at least 1 character; up to 60 characters
kindstring · requiredai_character: a photorealistic person who is not a real individual. illustrated: a drawn or animated character.
ai_character · illustrated
descriptionstring · requiredWho they are and how they look and sound. Passed to the creator unchanged.
at least 1 character; up to 20,000 characters
referencesarray · optionalUp to 20 completed, unexpired uploads. Remote URL references are not supported. An upload can be consumed by one request; retry that request with the same ID.
up to 20 items
references[].upload_idstring · requiredThe upload_id returned by prepare_reference_upload, after uploading the file's bytes.
filesarray · optionalFiles the user attached in their assistant, as temporary https download links (ChatGPT fills this in itself). The creator downloads each at the start of the run and uses it as a reference. To send a file's bytes yourself, use prepare_reference_upload and references instead.
up to 10 items
files[].download_urlstring · requiredA temporary https link to the file's bytes.
at least 1 character; up to 4,096 characters
files[].file_idstring · requiredThe assistant's own id for the file.
up to 200 characters
files[].mime_typestring · optionalThe file's MIME type, if known.
up to 200 characters
files[].file_namestring · optionalThe file's name, if known.
up to 200 characters
credit_capnumber · optionalOptional spending ceiling for this run. Can lower, never raise, the key's own per-run limit. Workspace balance and the key's remaining allowance also apply.
greater than 0
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/characters" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"request_id": "maya-001",
"name": "Maya",
"kind": "ai_character",
"description": "A 28-year-old product designer with short curly black hair, warm and direct. A calm, friendly voice."
}'Response · 202
{
"project_id": "character-id",
"run_id": "run-id",
"project_url": "https://app.ghostmotion.ai/characters/character-id",
"status": "accepted",
"poll_after_seconds": 15,
"character_id": "character-id"
}All response fields
character_idstring · requiredThe new character's id; also its workshop's project_id.
project_idstring · requiredThe project_id returned by creation or project listing.
at least 1 character
run_idstring · requiredThe run_id returned when a creation or edit is accepted.
at least 1 character
project_urlstring · requiredAbsolute URL.
statusstring · requiredThe run was accepted.
accepted
poll_after_secondsnumber · requiredSeconds to wait before checking the run.
Errors
unauthorized, permission_denied, not_found, invalid_request, insufficient_credits, allowance_exhausted, idempotency_conflict, request_in_progress, upload_not_ready, temporarily_unavailable, rate_limited, internal_error, temporarily_unavailable.
Stop a run
POST /api/v1/runs/{run_id}/stop
Request cancellation of one run on a project you created. Safe to repeat: it never stops a newer run.
Required permission: videos:write
stopping means cancellation was requested, not that it has finished. Poll get_run until it ends. Saved work remains, and work already done may still be charged. An already-ended run returns its terminal status.
Path parameters
run_idstring · requiredThe run_id returned when a creation or edit is accepted.
at least 1 character
Request body
No JSON body required.
Example request
curl --fail-with-body -X POST "https://app.ghostmotion.ai/api/v1/runs/$RUN_ID/stop" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY"Response · 200
{
"run_id": "run-id",
"status": "stopping",
"poll_after_seconds": 5
}All response fields
run_idstring · requiredThe run_id returned when a creation or edit is accepted.
at least 1 character
statusstring · requiredstopping or the run's terminal status.
stopping · completed · failed · cancelled · interrupted
poll_after_secondsnumber · optionalPresent when stopping: wait before checking again.
Errors
unauthorized, permission_denied, not_found, rate_limited, internal_error, temporarily_unavailable.
Prepare a reference upload
POST /api/v1/reference-uploads
Create a one-hour upload URL for an image, document, video, audio file, font, GLB or STL model. PUT raw file bytes to upload_url using the returned headers, then include upload_id in a creation or edit request.
Required permission: videos:write
Chat attachments are not visible to Ghostmotion until uploaded. There is no file-size cap (max_bytes is null); respect expires_at. upload_url is a private Google Cloud Storage resumable session: the file goes straight to storage, whatever its size, and storage answers the PUT (200 or 201 once it has the whole file). One PUT of the whole file works; so do Content-Range chunks with resume (a 308 reports the persisted Range). The upload is verified when a creation or edit first uses it; complete_reference_upload verifies it earlier and answers { upload_id, status: "ready", bytes }. size_bytes is optional and advisory. sha256 is optional and verified. upload_mode is accepted for compatibility and changes nothing. Models are inspected when attaching them to a real project, before starting the creator. Treat upload_url as a secret. Prepare a fresh upload if it expires or was consumed by another request. Preparing again creates another upload; this operation has no request_id.
Request body
filenamestring · requiredFile name including its extension. Cannot start with a dot or dash.
at least 1 character; up to 200 characters
content_typestring · optionalMIME type. The known filename extension takes precedence.
size_bytesinteger · optionalOptional expected file size in bytes.
greater than 0
upload_modestring · optionalAccepted for compatibility; every upload goes directly to storage.
proxy · resumable
sha256string · optionalOptional SHA-256 of the complete file, verified before use.
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/reference-uploads" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filename": "logo.png",
"content_type": "image/png"
}'Response · 201
{
"upload_id": "upload-id",
"upload_url": "https://storage.googleapis.com/upload/storage/v1/b/example-uploads-quarantine/o?uploadType=resumable&name=public%2Fupload-id%2Fsource&upload_id=example-session",
"method": "PUT",
"headers": {
"content-type": "image/png"
},
"max_bytes": null,
"expires_at": "2026-01-01T13:00:00.000Z",
"upload_mode": "resumable",
"chunk_bytes": 8388608,
"complete_url": "https://app.ghostmotion.ai/api/v1/reference-uploads/upload-id/complete",
"complete_by": "2026-01-01T13:00:00.000Z"
}All response fields
upload_idstring · requiredUpload identifier.
upload_urlstring · requiredAbsolute URL.
methodstring · requiredUse PUT to send the file.
PUT
headersobject · requiredheaders.content-typestring · requiredMIME type to send with the upload.
max_bytesnumber | null · requiredNull means there is no file-size cap.
expires_atstring · requiredISO 8601 UTC timestamp.
upload_modestring · optionalresumable: upload_url is a direct storage session.
resumable
chunk_bytesnumber · optionalRecommended chunk size, a multiple of GCS's 256 KiB granularity.
complete_urlstring · optionalAbsolute URL.
complete_bystring · optionalISO 8601 UTC timestamp.
Errors
unauthorized, permission_denied, not_found, invalid_request, rate_limited, internal_error, temporarily_unavailable.
Complete a direct reference upload
POST /api/v1/reference-uploads/{upload_id}/complete
Optional: verify an uploaded reference before using it (create_video and send_message run the same verification on first use). Rechecks the original issuing key, ownership and workspace permissions, and validates size, type, signature and optional checksum. Answers { upload_id, status: "ready", bytes }.
Required permission: videos:write
Use after storage acknowledges the final bytes, before complete_by. A lost response can be retried with the same upload_id. upload_not_ready means the upload is incomplete, expired or unavailable. Completion preserves the original preparation expiry; it does not grant an extra hour. No project is created and no credits are spent by completion.
Path parameters
upload_idstring · requiredThe upload_id returned by prepare_reference_upload for a resumable upload.
Request body
No JSON body required.
Example request
curl --fail-with-body -X POST "https://app.ghostmotion.ai/api/v1/reference-uploads/{upload_id}/complete" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY"Response · 200
{
"upload_id": "upload-id",
"status": "ready",
"bytes": 123456
}All response fields
upload_idstring · requiredThe completed upload identifier.
statusstring · requiredFile is ready to be used.
ready
bytesnumber · requiredNumber of bytes received.
Errors
unauthorized, permission_denied, not_found, invalid_request, upload_not_ready, rate_limited, internal_error, temporarily_unavailable.
Seedance API
Where your workspace has the Seedance API, these endpoints are available: BytePlus’s Seedance 2.5 task API at BytePlus’s own price.
Create a Seedance video
POST /api/v1/seedance/contents/generations/tasks
Generate a clip with Seedance 2.5 (dreamina-seedance-2-5-260628), in BytePlus ModelArk's own request shape. Returns the task id at once; poll get_seedance_task until it succeeds or fails. Costs exactly BytePlus's price, one credit per US dollar.
Required permission: videos:write
A BytePlus client only changes its base URL to {origin}/api/v1/seedance and its key. The price is BytePlus's list price for the tokens the clip used; when BytePlus can't make a task, the same task is made by another provider at the same price. Nothing made, nothing charged. The most a task could cost is held while it runs. Reuse request_id and the same body to retry without a second task.
Request body
request_idstring · optionalOptional retry identity: the same id and body returns the same task. Not part of BytePlus's API.
at least 1 character; up to 128 characters
modelstring · requiredThe model.
dreamina-seedance-2-5-260628
contentarray · requiredOne text item with the prompt, then any media items: up to 30 images, 10 videos and 10 audio clips.
content[].typestring · requiredWhat this item is.
text · image_url · video_url · audio_url
content[].textstring · optionalThe prompt, on the text item.
up to 5,000 characters
content[].image_urlobject · optionalcontent[].image_url.urlstring · requiredPublic https URL of the image. The model provider fetches it.
up to 2,048 characters
content[].video_urlobject · optionalcontent[].video_url.urlstring · requiredPublic https URL of the video (MP4 or MOV). The model provider fetches it.
up to 2,048 characters
content[].audio_urlobject · optionalcontent[].audio_url.urlstring · requiredPublic https URL of the audio. The model provider fetches it.
up to 2,048 characters
content[].rolestring · optionalAn image's role: first_frame (optionally with last_frame) makes those exact frames; reference_image guides a new shot. Videos are reference_video and audio reference_audio. A lone image without a role is the first frame.
first_frame · last_frame · reference_image · reference_video · reference_audio
ratiostring · optionalFrame shape. Defaults to 16:9, or adaptive (the input's shape) with a first frame or an edit or extension.
21:9 · 16:9 · 4:3 · 1:1 · 3:4 · 9:16 · adaptive
durationinteger · optionalLength in whole seconds, 4 to 30, or -1 for automatic. Defaults to 5 (automatic for an edit or extension).
minimum -1; maximum 30
resolutionstring · optionalOutput resolution. Defaults to 720p.
480p · 720p · 1080p
generate_audioboolean · optionalGenerate sound with the clip. Defaults to true.
watermarkboolean · optionalAdd a watermark. Defaults to false.
seedinteger · optionalSeed for repeatable results; -1 for random.
minimum -1; maximum 4294967295
omni_reference_task_typestring · optionalWith reference media: reference makes a new shot guided by them, edit changes the given video in place, extend continues it. Without it the model decides from the prompt.
reference · edit · extend
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/seedance/contents/generations/tasks" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "dreamina-seedance-2-5-260628",
"content": [
{
"type": "text",
"text": "A red kite over a beach at dusk, slow push-in"
},
{
"type": "image_url",
"image_url": {
"url": "https://example.com/first-frame.jpg"
},
"role": "first_frame"
}
],
"resolution": "1080p",
"duration": 6,
"request_id": "kite-001"
}'Response · 200
{
"id": "0190e2c4-1b2c-4d3e-8f40-123456789abc",
"model": "dreamina-seedance-2-5-260628",
"status": "queued",
"created_at": 1793361600,
"updated_at": 1793361600,
"resolution": "1080p",
"ratio": "adaptive",
"duration": 6,
"framespersecond": 24,
"generate_audio": true,
"credits": {
"price": null,
"charged": null,
"reserved": 4.89
},
"poll_after_seconds": 10
}All response fields
idstring · requiredThe id returned when the task was created.
at least 1 character
modelstring · requiredThe model.
statusstring · requiredqueued, running, then succeeded or failed.
queued · running · succeeded · failed
contentobject · optionalcontent.video_urlstring · requiredAbsolute URL.
content.video_url_expires_atstring · requiredISO 8601 UTC timestamp.
usageobject · optionalusage.completion_tokensnumber · requiredOutput tokens the clip used: what its price is counted in.
usage.total_tokensnumber · requiredThe same count, as BytePlus reports it.
errorobject · optionalerror.codestring · requiredStable failure code.
error.messagestring · requiredWhy it failed; a failed task is not charged.
created_atnumber · requiredUnix seconds.
updated_atnumber · requiredUnix seconds.
seednumber · optionalThe seed asked for.
resolutionstring · requiredOutput resolution.
ratiostring · requiredFrame shape.
durationnumber · requiredLength asked for, or -1 for automatic.
framespersecondnumber · optionalFrames per second.
generate_audioboolean · optionalWhether sound was asked for.
creditsobject · requiredcredits.pricenumber | null · requiredBytePlus's price for the clip, in credits (one per USD); null until it succeeds.
credits.chargednumber | null · requiredCredits charged; null until the task ends.
credits.reservednumber · requiredCredits held while the task runs.
poll_after_secondsnumber · optionalPresent while queued or running: wait this long before checking again.
Errors
unauthorized, permission_denied, not_found, invalid_request, insufficient_credits, allowance_exhausted, cap_too_low, idempotency_conflict, rate_limited, internal_error, temporarily_unavailable.
Check a Seedance video
GET /api/v1/seedance/contents/generations/tasks/{task_id}
Get a Seedance task in BytePlus's task shape: its status, and when it succeeded the clip's link, the tokens it used and its price in credits. While queued or running, wait poll_after_seconds before checking again.
Required permission: projects:read
Terminal states are succeeded and failed; a failed task's error says why, and it was not charged. content.video_url is a Ghostmotion link that expires after one hour: read the task again for a fresh one. It follows a redirect to temporary storage and needs no bearer header.
Path parameters
task_idstring · requiredThe id returned when the task was created.
at least 1 character
Query parameters
No query parameters required.
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/seedance/contents/generations/tasks/$TASK_ID" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY"Response · 200
{
"id": "0190e2c4-1b2c-4d3e-8f40-123456789abc",
"model": "dreamina-seedance-2-5-260628",
"status": "succeeded",
"created_at": 1793361600,
"updated_at": 1793361600,
"resolution": "1080p",
"ratio": "adaptive",
"duration": 6,
"framespersecond": 24,
"generate_audio": true,
"credits": {
"price": 3.435413,
"charged": 3.435413,
"reserved": 0
},
"content": {
"video_url": "https://app.ghostmotion.ai/api/v1/media/example-token",
"video_url_expires_at": "2026-11-01T13:02:00.000Z"
},
"usage": {
"completion_tokens": 293625,
"total_tokens": 293625
}
}All response fields
idstring · requiredThe id returned when the task was created.
at least 1 character
modelstring · requiredThe model.
statusstring · requiredqueued, running, then succeeded or failed.
queued · running · succeeded · failed
contentobject · optionalcontent.video_urlstring · requiredAbsolute URL.
content.video_url_expires_atstring · requiredISO 8601 UTC timestamp.
usageobject · optionalusage.completion_tokensnumber · requiredOutput tokens the clip used: what its price is counted in.
usage.total_tokensnumber · requiredThe same count, as BytePlus reports it.
errorobject · optionalerror.codestring · requiredStable failure code.
error.messagestring · requiredWhy it failed; a failed task is not charged.
created_atnumber · requiredUnix seconds.
updated_atnumber · requiredUnix seconds.
seednumber · optionalThe seed asked for.
resolutionstring · requiredOutput resolution.
ratiostring · requiredFrame shape.
durationnumber · requiredLength asked for, or -1 for automatic.
framespersecondnumber · optionalFrames per second.
generate_audioboolean · optionalWhether sound was asked for.
creditsobject · requiredcredits.pricenumber | null · requiredBytePlus's price for the clip, in credits (one per USD); null until it succeeds.
credits.chargednumber | null · requiredCredits charged; null until the task ends.
credits.reservednumber · requiredCredits held while the task runs.
poll_after_secondsnumber · optionalPresent while queued or running: wait this long before checking again.
Errors
unauthorized, permission_denied, not_found, rate_limited, internal_error, temporarily_unavailable.
Image and video model APIs
Where your workspace has Seedream 5.0, Nano Banana 2.1 or Gemini Omni Flash 1.1, these endpoints are available, each at its provider’s own list price.
Create Seedream images
POST /api/v1/seedream/images/generations
Generate or edit images with Seedream 5.0 (Pro, Lite or Flash) in BytePlus ModelArk's own images/generations request shape. Returns a task id at once; poll get_seedream_task until it succeeds or fails. Costs exactly BytePlus's list price per image, one credit per US dollar.
Required permission: videos:write
Unlike BytePlus's synchronous call, this returns a task to poll. BytePlus's prices: Pro $0.045 an image up to 2.61 million pixels (1.5K) and $0.09 above, plus $0.003 for each reference image after the first; Lite $0.035; Flash $0.018. Reference images are public https URLs (image, up to 10). Lite alone makes sets: with sequential_image_generation auto, up to max_images related images, each charged. When BytePlus can't make a task, the same task is made on another provider at BytePlus's price. Nothing made, nothing charged. Reuse request_id and the same body to retry.
Request body
request_idstring · optionalOptional retry identity: the same id and body returns the same task. Not part of BytePlus's API.
at least 1 character; up to 128 characters
modelstring · requiredThe Seedream 5.0 tier.
dola-seedream-5-0-pro-260628 · seedream-5-0-lite-260128 · dola-seedream-5-0-flash-260915
promptstring · requiredWhat to make, or how to change the reference images.
at least 1 character; up to 5,000 characters
imagearray · optionalReference images to edit or combine, as public https URLs. A single string is accepted too.
up to 10 items
sizestring · optionalPro and Flash: 1K, 1.5K or 2K; Lite: 2K or 4K; or WIDTHxHEIGHT up to the tier's largest size (each side at least 256). Defaults to 2K. Pro is priced by size: above 2.61 million pixels costs $0.09.
seedinteger · optionalSeed for repeatable results; -1 for random.
minimum -1; maximum 2147483647
sequential_image_generationstring · optionalLite only: auto makes a set of related images; disabled (the default) makes one.
disabled · auto
sequential_image_generation_optionsobject · optionalsequential_image_generation_options.max_imagesinteger · optionalThe most images an auto set may make. Each is charged.
minimum 1; maximum 15
watermarkboolean · optionalAdd a watermark. Defaults to false.
response_formatstring · optionalAlways url: images are returned as links.
url
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/seedream/images/generations" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "dola-seedream-5-0-pro-260628",
"prompt": "A paper-cut lighthouse on a stormy coast, warm window light",
"size": "2K",
"request_id": "lighthouse-001"
}'Response · 200
{
"id": "0190e2c4-1b2c-4d3e-8f40-223456789abc",
"model": "dola-seedream-5-0-pro-260628",
"status": "queued",
"created": 1793361600,
"size": "2K",
"credits": {
"price": null,
"charged": null,
"reserved": 0.09
},
"poll_after_seconds": 5
}All response fields
idstring · requiredThe id returned when the task was created.
at least 1 character
modelstring · requiredThe Seedream tier.
statusstring · requiredqueued, running, then succeeded or failed.
queued · running · succeeded · failed
creatednumber · requiredUnix seconds.
sizestring · optionalThe size asked for.
dataarray · optionalThe images, when it succeeded.
data[].urlstring · requiredAbsolute URL.
data[].sizestring · optionalWIDTHxHEIGHT, when known.
url_expires_atstring · optionalISO 8601 UTC timestamp.
usageobject · optionalusage.generated_imagesnumber · requiredImages made: what the price counts.
errorobject · optionalerror.codestring · requiredStable failure code.
error.messagestring · requiredWhy it failed; a failed task is not charged.
creditsobject · requiredcredits.pricenumber | null · requiredThe provider's list price for what was made, in credits (one per USD); null until it succeeds.
credits.chargednumber | null · requiredCredits charged; null until the task ends.
credits.reservednumber · requiredCredits held while the task runs.
poll_after_secondsnumber · optionalPresent while queued or running: wait this long before checking again.
Errors
unauthorized, permission_denied, not_found, invalid_request, insufficient_credits, allowance_exhausted, cap_too_low, idempotency_conflict, rate_limited, internal_error, temporarily_unavailable.
Check Seedream images
GET /api/v1/seedream/images/generations/{task_id}
Get a Seedream task: its status, and when it succeeded BytePlus's response shape (data[].url, usage.generated_images) and its price in credits.
Required permission: projects:read
Each data[].url is a Ghostmotion link that expires after one hour: read the task again for fresh ones. A failed task's error says why; it was not charged.
Path parameters
task_idstring · requiredThe id returned when the task was created.
at least 1 character
Query parameters
No query parameters required.
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/seedream/images/generations/$TASK_ID" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY"Response · 200
{
"id": "0190e2c4-1b2c-4d3e-8f40-223456789abc",
"model": "dola-seedream-5-0-pro-260628",
"status": "succeeded",
"created": 1793361600,
"size": "2K",
"credits": {
"price": 0.045,
"charged": 0.045,
"reserved": 0
},
"data": [
{
"url": "https://app.ghostmotion.ai/api/v1/media/example-token",
"size": "2048x2048"
}
],
"url_expires_at": "2026-11-01T13:02:00.000Z",
"usage": {
"generated_images": 1
}
}All response fields
idstring · requiredThe id returned when the task was created.
at least 1 character
modelstring · requiredThe Seedream tier.
statusstring · requiredqueued, running, then succeeded or failed.
queued · running · succeeded · failed
creatednumber · requiredUnix seconds.
sizestring · optionalThe size asked for.
dataarray · optionalThe images, when it succeeded.
data[].urlstring · requiredAbsolute URL.
data[].sizestring · optionalWIDTHxHEIGHT, when known.
url_expires_atstring · optionalISO 8601 UTC timestamp.
usageobject · optionalusage.generated_imagesnumber · requiredImages made: what the price counts.
errorobject · optionalerror.codestring · requiredStable failure code.
error.messagestring · requiredWhy it failed; a failed task is not charged.
creditsobject · requiredcredits.pricenumber | null · requiredThe provider's list price for what was made, in credits (one per USD); null until it succeeds.
credits.chargednumber | null · requiredCredits charged; null until the task ends.
credits.reservednumber · requiredCredits held while the task runs.
poll_after_secondsnumber · optionalPresent while queued or running: wait this long before checking again.
Errors
unauthorized, permission_denied, not_found, rate_limited, internal_error, temporarily_unavailable.
Create a Nano Banana image or Omni Flash video
POST /api/v1/gemini/models/{model}/tasks
Generate an image with Nano Banana 2.1 (gemini-nano-banana-2.1) or a video with Gemini Omni Flash 1.1 (gemini-omni-1.1-flash), with the Gemini API's own contents and generationConfig. Returns a task id at once; poll get_gemini_task until it succeeds or fails. Costs exactly Google's list price, one credit per US dollar.
Required permission: videos:write
Media are file_data parts with a public https file_uri; inline_data is refused. Nano Banana takes generationConfig.imageConfig (aspectRatio, imageSize 1K or 2K); Omni Flash takes generationConfig.videoConfig (aspectRatio 16:9 or 9:16, resolution 720p, durationSeconds 3 to 10, task generate, edit or extend). Each is charged exactly what Google bills: the tokens Google counts for it (input, text and thinking, and the image or video) at Google's list prices, shown in usage.tokens. A 1K image is about $0.04; 720p video about $0.10 a second. Sizes and resolutions Google publishes no single price for are not offered yet. When Google can't make a task, the same task is made on another provider at Google's price. Nothing made, nothing charged.
Path parameters
modelstring · requiredThe model, in the path.
gemini-nano-banana-2.1 · gemini-omni-1.1-flash
Request body
request_idstring · optionalOptional retry identity: the same id and body returns the same task. Not part of the Gemini API.
at least 1 character; up to 128 characters
contentsarray · requiredOne user turn: its parts are one text part and any file_data parts (Nano Banana: up to 14 images; Omni Flash: up to 3 images and 3 videos). inline_data is refused.
up to 1 items
contents[].rolestring · optionalAlways user.
user
contents[].partsarray · requiredA text part and file_data parts.
up to 18 items
contents[].parts[].textstring · optionalThe prompt: exactly one part carries it.
up to 5,000 characters
contents[].parts[].file_dataobject · optionalcontents[].parts[].file_data.file_uristring · requiredPublic https URL of an image or video. Fetched from a public address only.
up to 2,048 characters
contents[].parts[].file_data.mime_typestring · requiredThe file's type.
image/png · image/jpeg · image/webp · video/mp4 · video/quicktime
generationConfigobject · optionalgenerationConfig.responseModalitiesarray · optionalIMAGE for Nano Banana, VIDEO for Omni Flash; TEXT may be added.
up to 2 items
generationConfig.imageConfigobject · optionalgenerationConfig.imageConfig.aspectRatiostring · optionalNano Banana's frame shape. Left out, the model chooses.
21:9 · 16:9 · 3:2 · 4:3 · 5:4 · 1:1 · 4:5 · 3:4 · 2:3 · 9:16
generationConfig.imageConfig.imageSizestring · optionalNano Banana's size. Defaults to 1K. 4K is not offered until Google publishes one price for it.
1K · 2K
generationConfig.videoConfigobject · optionalgenerationConfig.videoConfig.aspectRatiostring · optionalOmni Flash's frame shape. Defaults to 16:9.
16:9 · 9:16
generationConfig.videoConfig.resolutionstring · optionalOmni Flash's resolution: 720p, the one Google publishes a price for.
720p
generationConfig.videoConfig.durationSecondsinteger · optionalThe length to ask for (default 8). Google sets the final length, 3 to 10 seconds; you pay for what is delivered. Not for an edit or extension.
minimum 3; maximum 10
generationConfig.videoConfig.taskstring · optionalgenerate (the default), edit the one given video, or extend it.
generate · edit · extend
generationConfig.seedinteger · optionalSeed for repeatable results.
minimum 0; maximum 2147483647
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/gemini/models/gemini-nano-banana-2.1/tasks" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [
{
"role": "user",
"parts": [
{
"text": "Turn this sketch into a glossy product photo on white"
},
{
"file_data": {
"file_uri": "https://example.com/sketch.png",
"mime_type": "image/png"
}
}
]
}
],
"generationConfig": {
"imageConfig": {
"aspectRatio": "1:1",
"imageSize": "2K"
}
},
"request_id": "sketch-001"
}'Response · 200
{
"id": "0190e2c4-1b2c-4d3e-8f40-323456789abc",
"model": "gemini-nano-banana-2.1",
"status": "queued",
"createTime": "2026-11-01T12:00:00.000Z",
"credits": {
"price": null,
"charged": null,
"reserved": 0.0504
},
"poll_after_seconds": 5
}All response fields
idstring · requiredThe id returned when the task was created.
at least 1 character
modelstring · requiredThe model.
statusstring · requiredqueued, running, then succeeded or failed.
queued · running · succeeded · failed
createTimestring · requiredISO 8601 UTC timestamp.
candidatesarray · optionalThe output, when it succeeded, as the Gemini API's candidate with file_data parts.
candidates[].contentobject · requiredcandidates[].content.rolestring · requiredmodel
candidates[].content.partsarray · requiredOne file_data part per output.
candidates[].content.parts[].file_dataobject · requiredcandidates[].content.parts[].file_data.file_uristring · requiredAbsolute URL.
candidates[].content.parts[].file_data.mime_typestring · requiredThe output's type.
candidates[].finishReasonstring · requiredSTOP
uri_expires_atstring · optionalISO 8601 UTC timestamp.
usageobject · optionalusage.imagesnumber · optionalImages made (Nano Banana).
usage.secondsnumber · optionalSeconds of video made (Omni Flash).
errorobject · optionalerror.codestring · requiredStable failure code.
error.messagestring · requiredWhy it failed; a failed task is not charged.
creditsobject · requiredcredits.pricenumber | null · requiredThe provider's list price for what was made, in credits (one per USD); null until it succeeds.
credits.chargednumber | null · requiredCredits charged; null until the task ends.
credits.reservednumber · requiredCredits held while the task runs.
poll_after_secondsnumber · optionalPresent while queued or running: wait this long before checking again.
Errors
unauthorized, permission_denied, not_found, invalid_request, insufficient_credits, allowance_exhausted, cap_too_low, idempotency_conflict, rate_limited, internal_error, temporarily_unavailable.
Check a Nano Banana or Omni Flash task
GET /api/v1/gemini/tasks/{task_id}
Get a Nano Banana or Omni Flash task: its status, and when it succeeded the image or video as file_data parts in the Gemini API's candidate shape, with usage and its price in credits.
Required permission: projects:read
Each file_uri is a Ghostmotion link that expires after one hour: read the task again for fresh ones. A failed task's error says why; it was not charged.
Path parameters
task_idstring · requiredThe id returned when the task was created.
at least 1 character
Query parameters
No query parameters required.
Example request
curl --fail-with-body "https://app.ghostmotion.ai/api/v1/gemini/tasks/$TASK_ID" \
-H "Authorization: Bearer $GHOSTMOTION_API_KEY"Response · 200
{
"id": "0190e2c4-1b2c-4d3e-8f40-323456789abc",
"model": "gemini-nano-banana-2.1",
"status": "succeeded",
"createTime": "2026-11-01T12:00:00.000Z",
"credits": {
"price": 0.0504,
"charged": 0.0504,
"reserved": 0
},
"candidates": [
{
"content": {
"role": "model",
"parts": [
{
"file_data": {
"file_uri": "https://app.ghostmotion.ai/api/v1/media/example-token",
"mime_type": "image/png"
}
}
]
},
"finishReason": "STOP"
}
],
"uri_expires_at": "2026-11-01T13:02:00.000Z",
"usage": {
"images": 1
}
}All response fields
idstring · requiredThe id returned when the task was created.
at least 1 character
modelstring · requiredThe model.
statusstring · requiredqueued, running, then succeeded or failed.
queued · running · succeeded · failed
createTimestring · requiredISO 8601 UTC timestamp.
candidatesarray · optionalThe output, when it succeeded, as the Gemini API's candidate with file_data parts.
candidates[].contentobject · requiredcandidates[].content.rolestring · requiredmodel
candidates[].content.partsarray · requiredOne file_data part per output.
candidates[].content.parts[].file_dataobject · requiredcandidates[].content.parts[].file_data.file_uristring · requiredAbsolute URL.
candidates[].content.parts[].file_data.mime_typestring · requiredThe output's type.
candidates[].finishReasonstring · requiredSTOP
uri_expires_atstring · optionalISO 8601 UTC timestamp.
usageobject · optionalusage.imagesnumber · optionalImages made (Nano Banana).
usage.secondsnumber · optionalSeconds of video made (Omni Flash).
errorobject · optionalerror.codestring · requiredStable failure code.
error.messagestring · requiredWhy it failed; a failed task is not charged.
creditsobject · requiredcredits.pricenumber | null · requiredThe provider's list price for what was made, in credits (one per USD); null until it succeeds.
credits.chargednumber | null · requiredCredits charged; null until the task ends.
credits.reservednumber · requiredCredits held while the task runs.
poll_after_secondsnumber · optionalPresent while queued or running: wait this long before checking again.
Errors
unauthorized, permission_denied, not_found, rate_limited, internal_error, temporarily_unavailable.
Upload and download URLs
The upload_url returned by the upload endpoint is a private Google Cloud Storage upload session: PUT the raw bytes there with the returned headers and keep the URL intact. Storage answers 200 or 201 once it has the whole file. complete_reference_upload answers upload_id, status: "ready" and bytes.
GET /api/v1/media/{token} retrieves a film using the opaque URL returned by a run. Follow redirects; the server may issue a 307 to temporary object-storage access. Add ?download=1 to request an attachment. No extra bearer header is needed for these signed URLs. They still depend on the issuing key and current project access.
Never publish signed URLs or treat them as permanent links. See the upload guide for the complete file workflow.