Skip to content

Image & video generation

Image and video models (Qwen-Image, Z-Image, Wan, Kling, Veo, …) do not run through chat completions or an OpenAI-style /v1/images/generations endpoint. They run through an asynchronous job queue with its own base URL:

https://console.inferel.ai/api/v1/queue

Authentication is the same API key you use for chat completions, sent as a bearer token. The flow is: (optionally) estimate the price → submit a job → poll it until it finishes → download the media from outcome.media_urls.

GET /apikey/models

Returns the generation model IDs:

Terminal window
curl https://console.inferel.ai/api/v1/queue/apikey/models \
-H "Authorization: Bearer $INFEREL_API_KEY"
{"model_ids": ["qwen-image-2.0", "z-image-turbo", "wan2.7-t2v", "..."]}

This catalog is separate from the LLM catalog at GET /v1/models — a generation model ID sent to chat completions returns 404 no endpoint for model, and vice versa. The list may occasionally include a model that is temporarily inactive; submitting to one returns a 404 naming the model as inactive, and nothing is charged.

Fetch one model for its parameter schema and pricing — the parameters listed there are exactly the keys accepted in payload when you submit:

Terminal window
curl https://console.inferel.ai/api/v1/queue/apikey/models/qwen-image-2.0 \
-H "Authorization: Bearer $INFEREL_API_KEY"
POST /apikey/requests/estimate

Same body as a submit; nothing is charged. Prices are returned in micro-USD (1,000,000 = $1.00):

Terminal window
curl https://console.inferel.ai/api/v1/queue/apikey/requests/estimate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $INFEREL_API_KEY" \
-d '{"model": "qwen-image-2.0", "payload": {"text": "a red apple on a table"}}'
{"model": "qwen-image-2.0", "price": 35000, "currency": "micro_usd"}

The estimate honors the same defaults the submit applies (resolution, duration, image count n, …), so for a given payload the estimate equals the charge.

POST /apikey/requests
Terminal window
curl https://console.inferel.ai/api/v1/queue/apikey/requests \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $INFEREL_API_KEY" \
-d '{"model": "qwen-image-2.0", "payload": {"text": "a red apple on a table"}}'
{"request_id": "3ea0a595-...", "model": "qwen-image-2.0", "status": "queued", "...": "..."}

Your organization’s balance is pre-charged the estimated price at submit time. If the job fails or is rejected, the charge is refunded automatically and exactly.

Fast image models often complete synchronously — the submit response itself may already have "status": "success" with the media in outcome. Video models take roughly 1–5 minutes; poll for those.

GET /apikey/requests/{request_id}

Poll until status is success, failed, or cancelled (every 5–10 seconds is plenty). On success, outcome.media_urls holds the generated files:

{
"request_id": "3ea0a595-...",
"status": "success",
"outcome": {
"media_urls": [
{"id": "0", "type": "image", "url": "https://storage.googleapis.com/..."}
]
}
}

Complete example:

import time, requests
BASE = "https://console.inferel.ai/api/v1/queue"
HEADERS = {"Authorization": "Bearer YOUR_INFEREL_API_KEY"}
job = requests.post(
f"{BASE}/apikey/requests",
headers=HEADERS,
json={"model": "wan2.7-t2v", "payload": {"prompt": "a golden retriever surfing at sunset"}},
).json()
while True:
r = requests.get(f"{BASE}/apikey/requests/{job['request_id']}", headers=HEADERS).json()
if r["status"] in ("success", "failed", "cancelled"):
break
time.sleep(10)
print(r["outcome"]) # media_urls[0].url is your generated file

Generated media is hosted on Inferel storage with public, unguessable URLs and is retained for 60 days — download anything you want to keep.

GET /apikey/requests

Paginated job history, scoped to the user who owns the API key — jobs submitted by other members of your organization (or via their keys) do not appear here, though they all bill to the same organization balance. Optional query filters: model_id, model_type (comma-separated, e.g. image,video), status (comma-separated, e.g. success,failed), created_after / created_before (unix seconds), plus limit (default 20, max 1000) and offset.

Input media (image-to-video, image editing)

Section titled “Input media (image-to-video, image editing)”

Models that take an input image or video accept any publicly fetchable URL in the relevant payload field. Note that some hosts (e.g. Wikimedia) block hotlink fetches from data centers — if a job fails on a URL your browser can open, re-host the file first. The simplest way is to upload it to Inferel storage:

POST /apikey/upload-url
Terminal window
curl https://console.inferel.ai/api/v1/queue/apikey/upload-url \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $INFEREL_API_KEY" \
-d '{"file_type": "jpg"}'
{"upload_url": "https://storage.googleapis.com/...(signed)...", "public_url": "https://storage.googleapis.com/..."}

file_type may be jpeg, jpg, png, mp4, mp3, or wav. Then PUT the file bytes to upload_url — the Content-Type header must be exactly <kind>/<file_type-as-passed> (so file_type: "jpg"Content-Type: image/jpg, not image/jpeg) or the signed upload is rejected with a 403:

Terminal window
curl -X PUT "$UPLOAD_URL" -H "Content-Type: image/jpg" --data-binary @input.jpg

Use the returned public_url in your job payload.

DELETE /apikey/requests/{request_id}/media

Permanently deletes a finished job’s generated media (and any input files you uploaded to Inferel storage), while preserving the job record, usage, and billing. Idempotent. Externally hosted input URLs are never touched. Already-served copies may persist in CDN caches for up to ~5 minutes after deletion.

Each job is charged a flat per-job price (per image, or per clip by resolution and duration) — not per token. The price appears on the model’s catalog page and via the estimate endpoint; charges show up in your balance history and monthly invoice like any other usage.