Skip to main content

Video Generation

Knox Chat exposes a dedicated asynchronous video generation API. Unlike chat completions, video jobs are submitted, polled until they finish, then downloaded as media files.

The typical flow is:

  1. List models — GET /v1/videos/models
  2. Submit a job — POST /v1/videos
  3. Poll status — GET /v1/videos/{jobId}
  4. Download the file — GET /v1/videos/{jobId}/content

All authenticated requests use the same base URL and API key as the rest of Knox Chat:

https://api.knox.chat/v1
Authorization: Bearer sk-...

Model Discovery

Video models are not the same catalog as /v1/models. Fetch the video catalog:

curl https://api.knox.chat/v1/videos/models

Each model advertises the options you may send on submit:

FieldUse it to constrain
idmodel
supported_aspect_ratiosaspect_ratio (16:9, 9:16, …)
supported_resolutionsresolution (720p, 1080p, 4K, …)
supported_sizessize (1280x720)
supported_durationsduration (seconds)
supported_frame_imagesframe_images.frame_type (first_frame, last_frame)
generate_audiogenerate_audio
seedseed
pricing_skusEstimated USD cost by mode, resolution, and audio

Unauthenticated catalog requests are limited to 60/minute and may be cached for 5 minutes. A Bearer token returns only the models that token is allowed to use.

You can also browse video models on the Models List.

Submit a Job

POST /v1/videos returns 202 Accepted with a job ID. That is not a finished video.

A prompt is required unless you attach image input (frame_images or an input_references item with type: "image_url").

curl -X POST https://api.knox.chat/v1/videos \
-H "Authorization: Bearer $KNOXCHAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/veo-3.1",
"prompt": "A serene mountain landscape at sunset, cinematic camera drift",
"aspect_ratio": "16:9",
"duration": 8,
"resolution": "720p",
"generate_audio": true
}'

Image to video

If the model lists supported_frame_images, you can pin the first and/or last frame:

{
"model": "google/veo-3.1",
"prompt": "Animate this still into a slow cinematic push-in",
"frame_images": [
{
"frame_type": "first_frame",
"image_url": { "url": "https://example.com/first-frame.png" }
}
]
}

Reference video

{
"model": "google/veo-3.1",
"prompt": "Keep the same camera motion, change the scene to night",
"input_references": [
{
"type": "video_url",
"video_url": { "url": "https://example.com/reference.mp4" }
}
]
}

Poll Until Ready

Poll GET /v1/videos/{jobId} (or the relative polling_url from submit) until status is terminal.

StatusWhat to do
pending / in_progressWait and poll again (every few seconds).
completedDownload the video. unsigned_urls and usage.cost are now present.
failedRead error. Failed jobs are not billed.
cancelled / expiredStop polling.
while True:
poll = requests.get(f"{API}/videos/{job_id}", headers=HEADERS)
poll.raise_for_status()
data = poll.json()
status = data["status"]
if status in {"completed", "failed", "cancelled", "expired"}:
break
time.sleep(5)

if data["status"] != "completed":
raise RuntimeError(data.get("error") or data["status"])

Jobs belong to the authenticated user. Polling another account's ID returns 404.

Download the Video

Once completed, fetch raw bytes. Follow redirects — the API may return 307 Temporary Redirect to a short-lived presigned URL. You can also use unsigned_urls from the poll response; those are Knox-hosted links, never upstream provider URLs.

curl -L "https://api.knox.chat/v1/videos/job-3c91a0e8b7d24f11/content" \
-H "Authorization: Bearer $KNOXCHAT_API_KEY" \
-o generated.mp4

Use ?index=0 (the default) when a job produced multiple files.

Downloading before completed returns:

{
"error": {
"code": 400,
"message": "Video content is not available until generation has completed"
}
}

Request Parameters

ParameterRequiredNotes
modelYesVideo model id from /v1/videos/models.
promptConditionalRequired unless image input is provided.
aspect_ratioNo16:9, 9:16, 1:1, 4:3, 3:4, 3:2, 2:3, 21:9, 9:21.
durationNoInteger seconds, at least 1.
resolutionNo480p, 720p, 768p, 1080p, 1K, 2K, 4K.
sizeNoExact WIDTHxHEIGHT, for example 1280x720.
generate_audioNoOnly if the model reports generate_audio: true.
seedNoRejected when the model reports seed: false.
callback_urlNoHTTPS webhook when the job reaches a terminal status.
frame_imagesNoframe_type must be first_frame or last_frame.
input_referencesNotype is image_url or video_url.

Values that are globally valid can still be rejected if the selected model does not list them.

Billing

Cost is estimated from the model's pricing_skus using duration, resolution, audio, and whether image or video input was attached. The submit call returns 402 if the account cannot cover the estimate:

{
"error": {
"code": 402,
"message": "Insufficient credits. Add more using https://knox.chat/credits"
}
}

When the job completes, usage.cost is the billed USD amount. Failed jobs are not charged.

Error Envelope

All video endpoints share this error shape:

{
"error": {
"code": 400,
"message": "Invalid request parameters"
}
}
CodeMeaning
202Job accepted (submit only). Poll for completion.
200Poll or catalog success. A poll with status: "failed" is still HTTP 200.
400Invalid parameters, or content requested before the job completed.
401Missing or invalid API key.
402Insufficient credits.
403Token is not allowed to use this video model.
404Unknown model or job (including another user's job).
429Rate limit exceeded.
500Internal server error.

Best Practices

  • Discover capabilities from /v1/videos/models instead of hard-coding resolutions or durations.
  • Treat submit as async: persist id, poll with backoff, then download.
  • Follow redirects on /content (curl -L, allow_redirects=True).
  • Prefer unsigned_urls or /content over storing any third-party URL — Knox never returns upstream provider URLs to clients.
  • Keep callback_url on HTTPS if you want a webhook instead of polling.
  • Handle 402 by sending the user to Credits.

API Reference