Quickstart#
You need an API key in the form sk-op-…. Create one in the console — it is shown once, and only its hash is stored. Your account holds the prepaid balance shared by all its keys; a key only sees its own jobs. Base URL for this deployment:
https://www.omniportapi.com/v1
Create a job, then poll it by id:
curl https://www.omniportapi.com/api/v1/videos \
-H "Authorization: Bearer $OMNIPORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "sora-2",
"prompt": "a paper boat drifting through a neon city",
"size": "720x1280",
"seconds": 8
}'
curl https://www.omniportapi.com/api/v1/videos/video_9f2c… \
-H "Authorization: Bearer $OMNIPORT_API_KEY"https://www.omniportapi.com/v1 works, because the paths, the Video object and the error envelope all follow that shape. If you only want generation, a plain fetch is enough.Authentication#
Every request carries one header. Missing, malformed or revoked keys get 401 authentication_error with code: invalid_api_key.
Authorization: Bearer sk-op-...
Credits are held by your account, so rotating a key never moves the balance. Jobs created with a key are only visible to that key — asking for another key's job returns 404 not_found_error, not 403, so job ids cannot be probed for validity.
OpenAI-compatible surface#
Use this surface if you already have OpenAI client code. Model names, size and seconds are translated to the generation pipeline internally.
Create a video
POST /api/v1/videos — JSON, or multipart/form-data when input_reference is a file.
| Field | Type | Required | Notes |
|---|---|---|---|
| model | string | no | Defaults to sora-2. Must be enabled here — see GET /api/v1/models. |
| prompt | string | yes | 1 to 8000 characters, trimmed. |
| size | string | no | Defaults to 720x1280. Any WxH is mapped to the nearest supported ratio (9:16, 16:9, 1:1, 3:4, 4:3, 2:3, 3:2, 21:9); ratios far from all of them are rejected. |
| seconds | 4 | 8 | 12 | no | Defaults to 4. Submitted to the pipeline as at least 5 seconds, and billed at that value. |
| input_reference | file | URL | no | PNG / JPEG / WebP up to 10 MB as a multipart file, or a publicly reachable http(s) URL as a JSON string. Guides the first frame. |
Response — 200 with the Video object:
{
"id": "video_9f2c…",
"object": "video",
"created_at": 1767225600,
"status": "queued",
"completed_at": null,
"error": null,
"expires_at": null,
"model": "sora-2",
"progress": 0,
"prompt": "a paper boat drifting through a neon city",
"remixed_from_video_id": null,
"seconds": "8",
"size": "720x1280"
}Statuses
| Status | Meaning |
|---|---|
| queued | Accepted, waiting for a worker. progress: 0. |
| in_progress | Scheduling or rendering. progress is an indication (10, then 50), not a measurement. |
| completed | Done. completed_at is set; the MP4 is available through /content. |
| failed | Failed or cancelled upstream. error carries code and message; the charge is refunded. |
List, fetch, download
| Call | Notes |
|---|---|
| GET /api/v1/videos | Newest first. limit (default 20, max 100), after (cursor id), order (asc | desc). Returns object: "list", data, first_id, last_id, has_more. |
| GET /api/v1/videos/{id} | One Video object, status synced on read. |
| GET /api/v1/videos/{id}/content | 302 to the MP4 on object storage. Before the job is completed it fails with 400 video_not_ready; variant=thumbnail is not supported. |
| DELETE /api/v1/videos/{id} | Deletes the job and refunds it if it has not finished. A job that is already rendering upstream cannot be cancelled — that returns 409 with video_in_progress. |
| GET /api/v1/models | Models enabled on this deployment, with display names. |
Native surface#
Native endpoints take the generation parameters directly instead of a model name. The scenario is chosen by the path — there is no field for it, and sending an unknown field is an error rather than a silent no-op.
Request fields
| Field | Type | Notes |
|---|---|---|
| resolution | string | Required. One of 480P, 768P, 1080P, 2K. Also selects the price tier. |
| aspectRatio | string | Required. For example 9:16 or 16:9. On upscale it must match the source video, otherwise the picture is re-framed. |
| prompt | string | Required for fl2v and r2v; optional on upscale (a neutral description is used). |
| durationSeconds | number | 5 to 15, default 5. Not accepted on upscale, where duration follows the source video. |
| negativePrompt | string | Optional, generation scenarios only. |
| seed | integer ≥ 0 | Optional, generation scenarios only. |
| imageUrls | string[] | Public http(s) URLs. fl2v takes up to 2 (first and last frame); r2v up to 8. |
| videoUrls | string[] | r2v up to 3; upscale exactly 1. |
| audioUrls | string[] | r2v only, up to 3. |
First / last frame — fl2v
curl https://www.omniportapi.com/api/v1/fl2v/generations \
-H "Authorization: Bearer $OMNIPORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "the boat drifts forward, camera slowly pushes in",
"aspectRatio": "9:16",
"resolution": "1080P",
"durationSeconds": 8,
"imageUrls": ["https://cdn.example.com/frame.png"]
}'Upscale — one video in, one higher tier out
curl https://www.omniportapi.com/api/v1/upscale/generations \
-H "Authorization: Bearer $OMNIPORT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"aspectRatio": "16:9",
"resolution": "2K",
"videoUrls": ["https://cdn.example.com/clip.mp4"]
}'A typical loop is: validate the prompt at 480P, then upscale the clip you liked to 2K, instead of paying for two full renders.
Accepted request
201 — the job id is the upstream task id, so polling can start immediately:
{ "id": "task_8c41…", "status": "queued", "scenario": "fl2v" }Task object
GET /api/v1/generations/{id}
{
"id": "task_8c41…",
"status": "succeeded",
"error": null,
"scenario": "fl2v",
"resolution": "1080P",
"aspectRatio": "9:16",
"durationSeconds": 8,
"createdAt": 1767225600,
"updatedAt": 1767225900,
"artifacts": [{ "kind": "video", "url": "https://…/out.mp4", "filename": "out.mp4" }]
}Native statuses are passed through as the pipeline reports them: queued, scheduling, running, succeeded, failed, cancelled. artifacts is only filled for succeeded jobs, and is only present on the detail call.
List tasks
GET /api/v1/generations?page_num=1&page_size=20 returns { tasks, total, page_num, page_size }. Page size is capped at 100. The list is newest first and does not include artifacts — fetching artifacts for every row would make the list as slow as the slowest job.
Pricing and billing#
Prices are quoted in credits (1 credit = 1 US cent) per second of generated video, with a minimum per job. The current numbers are on the pricing section of the overview page; they come from this deployment's price list, not from a constant in the docs.
- The full amount in credits is held when the job is created. If your account balance is short, the request fails with
402 insufficient_quotaand no job is queued. - OpenAI surface: billed per model. Native surface: billed per resolution tier. The two price lists are maintained separately.
- The pipeline renders at least 5 seconds, so
seconds: 4is billed as 5. - Failed or cancelled jobs are refunded automatically. The refund is recorded per job and happens once, no matter how often the job is polled.
- Upscale is billed as one 5-second unit; the real duration comes from the source video.
Buying credits#
Credits are prepaid balance on your account, and every key under that account spends from the same balance. Packs are priced in US dollars and credited as credits (1 credit = 1 US cent); the list on the overview page is the same one shown in the console — one table, so anything you see in one place is purchasable in the other.
- Payment is handled by Paddle. A pack marked unavailable on this deployment has no price configured for it — contact support.
- The balance is credited only when Paddle confirms the transaction. Returning from the checkout page is not a confirmation.
- Credits never expire. Packs with a bonus credit more than they charge, and the bonus is already included in the amount you receive.
- Topping up credits the account, not a key: rotating keys does not move or lose the balance.
Errors#
Every failure, including 5xx, is the same envelope. param and code are optional.
{
"error": {
"message": "resolution must be one of 480P, 768P, 1080P, 2K.",
"type": "invalid_request_error",
"param": "resolution",
"code": "invalid_value"
}
}| Status | type | When |
|---|---|---|
| 400 | invalid_request_error | Bad parameter. code narrows it down: invalid_value, missing_required_parameter, unsupported_parameter, too_many_values, model_not_found, model_disabled, resolution_not_found, resolution_disabled. |
| 401 | authentication_error | Missing, malformed, revoked or inactive key (invalid_api_key). |
| 402 | insufficient_quota | The account balance does not cover this job (insufficient_quota), or this key has reached its own credit limit (key_credit_limit_exceeded). |
| 404 | not_found_error | Unknown job id, or a job that belongs to another key. |
| 409 | invalid_request_error | The job already started rendering and cannot be deleted (video_in_progress). |
| 429 | rate_limit_error | The pipeline is rate limiting. Retry shortly. |
| 5xx | server_error | Ours, or the pipeline is unreachable. Anything charged for a rejected create is refunded before the error is returned. |
Limits#
input_referenceupload: 10 MB, PNG / JPEG / WebP only.- Prompt: 8000 characters per request.
- Reference media: at most 8 images, 3 videos and 3 audio clips per
r2vrequest; 2 images perfl2vrequest. - Reference URLs must be public
http(s). Private addresses are refused — the pipeline downloads them, and it cannot reach your intranet. - List endpoints: at most 100 items per page.