Skip to content

OpenAI Video Format

POST /v1/videos and GET /v1/videos/{task_id} are the OpenAI video dialect of the same asynchronous task API described in Create a video task and Get a video task. The submit body uses the same fields; the difference is the response: both the submit response and the GET return an OpenAI video object rather than the task record. This is the format Sora models use, and it is what an OpenAI SDK’s video client expects.

MethodPathPurpose
POST/v1/videosSubmit a generation task
GET/v1/videos/{task_id}Read the task as an OpenAI video object
GET/v1/videos/{task_id}/contentDownload the finished video bytes
POST/v1/videos/{video_id}/remixCreate a task derived from an existing video
Terminal window
curl https://api.routeapi.ai/v1/videos \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "sora-2",
"prompt": "A calico cat playing a piano on stage",
"size": "1280x720",
"seconds": "4"
}'

The request fields are the ones on Create a video task: model, prompt, seconds / duration, size, input_reference, images, and metadata. input_reference is honored by the models that support a reference image. multipart/form-data is accepted as well, so a file can be uploaded directly:

Terminal window
curl https://api.routeapi.ai/v1/videos \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-F "model=sora-2" \
-F "prompt=A calico cat playing a piano on stage" \
-F "size=1280x720" \
-F "seconds=4" \
-F "input_reference=@cat.jpg"
{
"id": "task_G2RdWGt4t1nck5fYqTjwREKJo8Ze4RAV",
"task_id": "task_G2RdWGt4t1nck5fYqTjwREKJo8Ze4RAV",
"object": "video",
"model": "sora-2",
"status": "queued",
"progress": 0,
"created_at": 1792048458
}

id is the RouteAPI task ID. task_id is a compatibility alias and may be absent in future responses; read id. Always address the task by this id; identifiers echoed inside the stored upstream payload are not the value to poll or download with.

The submit response also carries an X-New-Api-Other-Ratios header with the billing multipliers that were applied, for example {"seconds":5,"size":1} for a Sora request.

GET /v1/videos/{task_id}
Terminal window
curl https://api.routeapi.ai/v1/videos/task_G2RdWGt4t1nck5fYqTjwREKJo8Ze4RAV \
-H "Authorization: Bearer $ROUTEAPI_KEY"
{
"id": "task_G2RdWGt4t1nck5fYqTjwREKJo8Ze4RAV",
"task_id": "task_G2RdWGt4t1nck5fYqTjwREKJo8Ze4RAV",
"object": "video",
"model": "seedance-2.0",
"status": "completed",
"progress": 100,
"created_at": 1792048458,
"completed_at": 1792048569,
"metadata": { "url": "https://...mp4" }
}
FieldDescription
idRouteAPI task ID
objectAlways "video"
modelModel that ran the task
statusqueued, in_progress, completed, or failed
progressInteger 0–100
created_at / completed_atUnix seconds. Some adaptors fill completed_at with the row’s last update time, so decide completion from status, not from this field
expires_at, size, seconds, remixed_from_video_idPresent only when the provider reports them
metadata.urlVideo URL for providers that report one through the video object. Use /v1/videos/{task_id}/content if you want the bytes regardless of provider
error.code / error.messageSet when the task failed, for example expired or cancelled for Seedance

The upstream payload’s own fields (such as expires_at or size) appear only when the provider returns them; the set above is what RouteAPI guarantees to map. On Gemini and Vertex channels the task’s status is refreshed from the provider before this object is built.

The task is scoped to the API Key’s user: an id that belongs to another account is reported as task_not_exist, exactly as on the task-record endpoint.

GET /v1/videos/{task_id}/content

Returns the video bytes, streamed through RouteAPI. The response carries Cache-Control: public, max-age=86400 and the upstream Content-Type. The route accepts an API Key or a console session and only serves tasks owned by the caller. See Download the video for the error codes and a curl example.

POST /v1/videos/{video_id}/remix

Creates a new task from an existing video. RouteAPI looks up video_id among your tasks, derives the model from it, and pins the request to the channel that produced the original video so the result stays compatible.

Terminal window
curl https://api.routeapi.ai/v1/videos/task_G2RdWGt4t1nck5fYqTjwREKJo8Ze4RAV/remix \
-H "Authorization: Bearer $ROUTEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{ "prompt": "The same cat, now wearing a tiny hat" }'

Remix is meaningful for the Sora family (sora-2, sora-2-pro), where the request is forwarded to the upstream’s own remix endpoint and prompt is required. For other platforms the request is still accepted, the model and channel are resolved from the origin task, and the task is built by that model’s adaptor — but the upstream never receives a remix instruction, so the new task is an ordinary generation, not a derivative of video_id.

Remix inherits the origin task’s billing multipliers where the adaptor recorded them, so the new task is priced like the original rather than from defaults.

Sora models use this endpoint family directly; the parameters that matter are size and seconds.

ModelAccepted sizes
sora-2720x1280, 1280x720
sora-2-pro720x1280, 1280x720, 1792x1024, 1024x1792
  • An unsupported size returns 400 with code invalid_size before anything is reserved.
  • Defaults are 720x1280 and 4 seconds.
  • seconds (or duration) is validated the same way as every other video model: negative values and values above 3600 are rejected with 400 invalid_seconds.
  • input_reference is forwarded as the reference image; JSON and multipart submissions both work.
  • Billing is computed from seconds and size: a 1792x1024 or 1024x1792 output costs a size multiplier (1.666667) over the base size, on top of the per-second rate. The applied multipliers are returned in X-New-Api-Other-Ratios.
  • Remix is supported on this family.

Availability and price are per account — confirm the model appears in GET /v1/models and check the model detail page.

The task error shape applies once routed:

StatusCodeMeaning
400invalid_requestMissing or blank prompt, malformed body, or a field with the wrong type
400invalid_secondsduration / seconds is negative or above 3600
400invalid_sizeThe model does not accept this size (for example an unsupported Sora size)
400task_not_existThe video_id in a remix is unknown or belongs to another account
400task_source_unavailableThe origin task’s channel was deleted or disabled, or its adaptor is missing
400asset_remix_conflictA remix of a non-asset task tried to reference an asset:// material
429—Upstream capacity exhausted; the message is replaced with a generic retry notice
500build_request_failed, do_request_failedThe task could not be converted or the upstream connection failed
500convert_to_openai_video_failedThe stored task data could not be converted into a video object
501not_implementedGET /v1/videos/{task_id} on a platform whose adaptor has no OpenAI video conversion
anyfail_to_fetch_taskThe upstream answered the submit with a non-200 status; the HTTP status is the upstream’s

Requests rejected before routing use the standard {"error": {"message": ..., "type": "error", "code": ...}} body instead.