OmniPort API documentation

Two surfaces, one key: an OpenAI-compatible Videos API for existing SDKs, and native generation endpoints that speak resolution tiers. Everything is asynchronous — submit, poll, download.

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"
No SDK required. Any OpenAI client pointed at 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.

FieldTypeRequiredNotes
modelstringnoDefaults to sora-2. Must be enabled here — see GET /api/v1/models.
promptstringyes1 to 8000 characters, trimmed.
sizestringnoDefaults 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.
seconds4 | 8 | 12noDefaults to 4. Submitted to the pipeline as at least 5 seconds, and billed at that value.
input_referencefile | URLnoPNG / 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

StatusMeaning
queuedAccepted, waiting for a worker. progress: 0.
in_progressScheduling or rendering. progress is an indication (10, then 50), not a measurement.
completedDone. completed_at is set; the MP4 is available through /content.
failedFailed or cancelled upstream. error carries code and message; the charge is refunded.
Polling is the API. There are no webhooks and no callbacks. Every read of a job refreshes its status from the pipeline before answering, so a tight polling loop sees the change as soon as it happens. One job per poll; keep the interval at a second or more.

List, fetch, download

CallNotes
GET /api/v1/videosNewest 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}/content302 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/modelsModels 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

FieldTypeNotes
resolutionstringRequired. One of 480P, 768P, 1080P, 2K. Also selects the price tier.
aspectRatiostringRequired. For example 9:16 or 16:9. On upscale it must match the source video, otherwise the picture is re-framed.
promptstringRequired for fl2v and r2v; optional on upscale (a neutral description is used).
durationSecondsnumber5 to 15, default 5. Not accepted on upscale, where duration follows the source video.
negativePromptstringOptional, generation scenarios only.
seedinteger ≥ 0Optional, generation scenarios only.
imageUrlsstring[]Public http(s) URLs. fl2v takes up to 2 (first and last frame); r2v up to 8.
videoUrlsstring[]r2v up to 3; upscale exactly 1.
audioUrlsstring[]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.

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.

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"
  }
}
StatustypeWhen
400invalid_request_errorBad 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.
401authentication_errorMissing, malformed, revoked or inactive key (invalid_api_key).
402insufficient_quotaThe account balance does not cover this job (insufficient_quota), or this key has reached its own credit limit (key_credit_limit_exceeded).
404not_found_errorUnknown job id, or a job that belongs to another key.
409invalid_request_errorThe job already started rendering and cannot be deleted (video_in_progress).
429rate_limit_errorThe pipeline is rate limiting. Retry shortly.
5xxserver_errorOurs, or the pipeline is unreachable. Anything charged for a rejected create is refunded before the error is returned.

Limits#