Skip to content
XPNCORE

Documentation

Video generation API

Make videos from a description or an image with POST /v1/videos, follow their status and download the file.

Videos are made in the background: send the request, check the status until it is done, then download the file. The API keeps the shape of OpenAI's Videos API and adds frame ratio, resolution and first/last frames.

Key points

  • Find video models with GET /v1/models: they list videos.generations in endpoints.
  • POST /v1/videos returns an id at once; the status moves queued → in_progress → completed or failed.
  • Download with GET /v1/videos/{id}/content; a video is kept until expires_at.
  • Price = base price × length weight × resolution weight; you are charged only once the video is saved.
  • A failed, cancelled or timed-out video is never charged.

How it works#

Videos take time, so the API works as a background task, like OpenAI's Videos API:

  1. Create: POST /v1/videos returns a video object at once, queued, with its id.
  2. Follow: call GET /v1/videos/{id} every 10–15 seconds. status moves to in_progress, then completed or failed.
  3. Download: once completed, fetch the file with GET /v1/videos/{id}/content.

A video usually takes 1–3 minutes. One not done within the time limit (currently 20 minutes) stops with VIDEO_TIMEOUT, and you are not charged.

Make your first video#

The code below does all three steps: it makes an 8-second 16:9 video at 720p, waits for it, then saves it as boat.mp4. Take VIDEO_MODEL_ID from GET /v1/models: video models list videos.generations in endpoints.

POST /v1/videos → GET /v1/videos/{id} → GET /v1/videos/{id}/content
# 1. Create the video
VIDEO_ID=$(curl -s https://api.xpncore.com/v1/videos \
  -H "Authorization: Bearer $XPNCORE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "VIDEO_MODEL_ID",
    "prompt": "A paper boat drifting down a rainy neon street, slow dolly shot",
    "seconds": "8",
    "size": "1280x720"
  }' | jq -r '.id')

# 2. Check every 10 seconds until it is completed or failed
while true; do
  STATUS=$(curl -s https://api.xpncore.com/v1/videos/$VIDEO_ID \
    -H "Authorization: Bearer $XPNCORE_API_KEY" | jq -r '.status')
  echo "$STATUS"
  case "$STATUS" in completed|failed) break ;; esac
  sleep 10
done

# 3. Download the file
curl -L https://api.xpncore.com/v1/videos/$VIDEO_ID/content \
  -H "Authorization: Bearer $XPNCORE_API_KEY" \
  -o boat.mp4
200 OK · POST /v1/videos
{
  "id": "4c102af4-5d04-444c-846e-05f1eefccf7b",
  "object": "video",
  "model": "VIDEO_MODEL_ID",
  "status": "queued",
  "progress": 0,
  "created_at": 1759550400,
  "completed_at": null,
  "expires_at": null,
  "seconds": "8",
  "size": "1280x720",
  "prompt": "A paper boat drifting down a rainy neon street, slow dolly shot",
  "remixed_from_video_id": null,
  "error": null
}

Create parameters#

Send JSON, or multipart/form-data when you attach image files. The body is limited to 48 MB. An option the model does not offer is refused with a 422 VIDEO_OPTION_UNSUPPORTED that says why.

modelstringRequired

A video model ID from GET /v1/models.

promptstringRequired

What the video shows, up to 4,000 characters: subject, action, setting, camera movement, sound.

secondsstring | integerOptional

Length, for example "4", "6", "8". duration works too. Defaults to the model's own (usually 8).

sizestringOptional

WIDTHxHEIGHT, for example 1280x720 (16:9, 720p) or 720x1280 (9:16, 720p). You can send aspect_ratio and resolution instead.

aspect_ratiostringOptional

Frame ratio, for example 16:9 or 9:16.

resolutionstringOptional

For example 720p, 1080p, 4k. Higher resolutions usually come at 8 seconds only.

input_referencefile | stringOptional

An image to start from (see Video from an image). Also accepted as first_frame or image.

last_framefile | stringOptional

An image to end on; needs a first frame.

reference_imagesarrayOptional

Up to 3 reference images for a character or style. Not combined with first or last frames, and usually at 8 seconds only.

negative_promptstringOptional

What should not appear, up to 1,000 characters, when the model supports it.

seedintegerOptional

0 to 4294967295, for more repeatable results, when the model supports it.

Ratio, length and resolution#

size becomes a frame ratio and a resolution: the shorter side is the resolution.

  • 1280x720 → 16:9, 720p · 720x1280 → 9:16, 720p
  • 1920x1080 → 16:9, 1080p · 1080x1920 → 9:16, 1080p
  • 3840x2160 → 16:9, 4k

Each model offers its own choices. The most common: 16:9 and 9:16; 4, 6 or 8 seconds; 720p at any length, 1080p and 4k at 8 seconds only. Leave the fields out and the model uses its defaults (usually 16:9, 8 seconds, 720p). Most models make videos with sound.

Video from an image#

Send an image in input_reference and the video starts from exactly that frame. The image can be:

  • a file part in multipart (as in the sample),
  • a data URL data:image/png;base64,... or a base64 string in a JSON body,
  • a public https:// link (fetched within 20 seconds).

It must be PNG, JPEG or WebP, up to 10 MB. Match the video's frame ratio for the best result. Add last_frame to choose the closing frame as well.

POST /v1/videos · multipart/form-data
curl https://api.xpncore.com/v1/videos \
  -H "Authorization: Bearer $XPNCORE_API_KEY" \
  -F model=VIDEO_MODEL_ID \
  -F prompt="The boat slowly sails away as the rain stops" \
  -F seconds=8 \
  -F size=1280x720 \
  -F [email protected]

Follow the status#

The video object keeps the shape of OpenAI's Videos API:

  • queued: waiting its turn.
  • in_progress: being made; progress gives the percentage (0–100).
  • completed: done; the file can be downloaded until expires_at.
  • failed: it could not be made; error.code and error.message say why. A failed video is not charged.

Times are Unix seconds. Check every 10–15 seconds; checking more often does not make the video faster.

200 OK · GET /v1/videos/{id}
{
  "id": "4c102af4-5d04-444c-846e-05f1eefccf7b",
  "object": "video",
  "model": "VIDEO_MODEL_ID",
  "status": "completed",
  "progress": 100,
  "created_at": 1759550400,
  "completed_at": 1759550492,
  "expires_at": 1760155292,
  "seconds": "8",
  "size": "1280x720",
  "prompt": "A paper boat drifting down a rainy neon street, slow dolly shot",
  "remixed_from_video_id": null,
  "error": null
}

Download the video#

GET /v1/videos/{id}/content returns the video file itself (usually video/mp4) and supports the Range header, so it can be played or resumed from the middle.

  • A video is kept until expires_at (currently 7 days after it is made), then deleted. Download and keep your own copy for longer use.
  • Not ready yet: 404 VIDEO_NOT_READY. Deleted or expired: 404 VIDEO_GONE.
  • Videos made through the API and in Chat AI › Videos share one store per account (currently 2 GB).

List and delete videos#

  • GET /v1/videos lists your videos, newest first. Parameters: limit (1–100, default 20), after (the last id of the previous page), order (desc or asc). The response has data, first_id, last_id and has_more.
  • DELETE /v1/videos/{id}: a video still queued is cancelled and refunded; a finished one has its file deleted to free space. A video being made cannot be cancelled (409 VIDEO_NOT_CANCELLABLE).
GET /v1/videos · DELETE /v1/videos/{id}
# The 20 newest videos
curl "https://api.xpncore.com/v1/videos?limit=20" \
  -H "Authorization: Bearer $XPNCORE_API_KEY"

# The next page: the videos after the last id of this one
curl "https://api.xpncore.com/v1/videos?limit=20&after=LAST_ID" \
  -H "Authorization: Bearer $XPNCORE_API_KEY"

# Delete a video (a queued one is cancelled and refunded)
curl -X DELETE https://api.xpncore.com/v1/videos/$VIDEO_ID \
  -H "Authorization: Bearer $XPNCORE_API_KEY"

Pricing and billing#

You pay per video: base price × length weight × resolution weight. A 4-second video costs less than an 8-second one, and 1080p or 4k costs more than 720p.

  1. On create, the video's price is held in your wallet.
  2. You are charged only once the file is saved, never more than the hold.
  3. A failed, cancelled or timed-out video releases the whole hold.

Each model's base price is on the Models & pricing page; the exact price of every choice shows in Chat AI › Videos before you make one.

Common errors#

Refusals on create (nothing is held):

  • 402 INSUFFICIENT_FUNDS: the wallet balance does not cover the video.
  • 409 VIDEO_TOO_MANY_ACTIVE: the most videos you can make at once (currently 3) are in progress; wait for one to finish.
  • 409 VIDEO_STORAGE_FULL: your video store is full; delete older videos.
  • 422 VIDEO_OPTION_UNSUPPORTED: the model does not offer that ratio, length, resolution or image input.
  • 400 CONTENT_POLICY_BLOCKED: the prompt breaks the content policy.
  • 503 MODEL_PRICING_UNAVAILABLE: the model has no price yet and cannot be used.

Codes in error.code of a failed video (never charged):

  • VIDEO_CONTENT_BLOCKED: the safety filter refused the content; rewrite the prompt or change the image.
  • VIDEO_TIMEOUT: not done within the time limit.
  • VIDEO_UPSTREAM_FAILED, VIDEO_NO_CAPACITY: the model is busy or unavailable; retry in a few minutes.
  • VIDEO_DOWNLOAD_FAILED, VIDEO_TOO_LARGE, VIDEO_NOT_VIDEO: the result could not be fetched or was not valid.
  • VIDEO_CANCELLED: you cancelled it.

Compatible endpoint#

Besides /v1/videos, the gateway takes POST /v1/video/generations and GET /v1/video/generations/{id} for tools that expect that shape. They take the same parameters, but the object has object: "video.generation" and the statuses queued, processing, succeeded, failed. When done, video_url points to /v1/videos/{id}/content (API key required).

To try ideas without code, or to extend a video you made, use Chat AI › Videos in the dashboard.