Grok Video Generation
Create fixed 15-second grok-1.5-video reference-image videos: request contract, image URLs, polling, errors, and a reusable JavaScript example.
Grok Video Generation Developer Guide
This page is the only public integration contract for grok-1.5-video in APIOne. It is intentionally self-contained so it can be supplied directly to an AI coding tool or implementation team.
Call only the APIOne endpoints documented here and send the public model name exactly as grok-1.5-video. Do not replace it with aliases or call any other service directly.
Integration summary
| Item | Fixed rule |
|---|---|
| Base URL | https://apione.org |
| Create task | POST /xai/v1/videos/generations |
| Poll task | GET /xai/v1/videos/{request_id} |
| Authentication | Authorization: Bearer <APIOne API Key> |
| Model | grok-1.5-video |
| Duration | 15 seconds; send duration: 15 |
| Input | Public image URLs in JSON; do not use multipart/form-data |
| Image count | One image, or two to seven ordered reference images |
| Text-to-video | At least one reference image is currently required |
Create and manage the API key in APIOne. Store it only in a server-side environment variable or a secure secret store—never in a browser bundle, mobile app, repository, or log.
Create a video
POST https://apione.org/xai/v1/videos/generations
Authorization: Bearer <APIOne API Key>
Content-Type: application/jsonRequest fields
| Field | Type | Required | Allowed value / limit | Description |
|---|---|---|---|---|
model | string | Yes | Exactly grok-1.5-video | The public model name. |
prompt | string | Yes | Non-empty string | Describe the scene, movement, camera, timing, and style. |
image | object | One of image or reference_images | { "url": "http(s)://..." } | Use for a single reference image. |
reference_images | array | One of image or reference_images | 2–7 items | Use for multiple images; every item is { "url": "http(s)://..." }. |
duration | integer | Yes | Exactly 15 | Video duration is fixed at 15 seconds. |
aspect_ratio | string | Yes | 2:3, 3:2, 1:1, 16:9, 9:16 | Output aspect ratio. The field name must be aspect_ratio. |
resolution | string | Yes | 720p, 1080p | Output resolution. Use a lowercase p. |
Do not send size, ratio, input_reference, file bytes, Base64/Data URLs, or multipart/form-data to this endpoint. They are not part of this public request format.
Reference-image URLs and ordering
- Each image must be an
http://orhttps://URL that remains publicly retrievable for the duration of the task. HTTPS and non-expiring URLs are recommended for production. - Do not use local paths, private-network URLs, browser object URLs, Base64/Data URLs, or URLs that need cookies or interactive sign-in.
- Send either
imageorreference_images, never both. - In
reference_images, array position defines the prompt reference: first image is@img1, second is@img2, through@img7. - Refer to images in
promptwith the fixed spelling@img1,@img2, and so on. - One reference image supports
720por1080p. Two to seven reference images support720ponly; a multi-reference request with1080preturns400.
Single-image request
curl --request POST 'https://apione.org/xai/v1/videos/generations' \
--header 'Authorization: Bearer sk-xxxxxxxxxxxx' \
--header 'Content-Type: application/json' \
--data '{
"model": "grok-1.5-video",
"prompt": "Have the character in @img1 turn slowly as the camera moves smoothly from close-up to medium shot in cinematic natural light.",
"image": {
"url": "https://files.example.com/character.png"
},
"duration": 15,
"aspect_ratio": "16:9",
"resolution": "1080p"
}'Multiple-reference request
Array order determines the relationship of @img1, @img2, and @img3. Multiple references must use 720p.
curl --request POST 'https://apione.org/xai/v1/videos/generations' \
--header 'Authorization: Bearer sk-xxxxxxxxxxxx' \
--header 'Content-Type: application/json' \
--data '{
"model": "grok-1.5-video",
"prompt": "Gradually transform and shatter @img1 into @img2, then transition smoothly into @img3 while keeping the subject centered.",
"reference_images": [
{ "url": "https://files.example.com/one.png" },
{ "url": "https://files.example.com/two.png" },
{ "url": "https://files.example.com/three.png" }
],
"duration": 15,
"aspect_ratio": "16:9",
"resolution": "720p"
}'Create response
A successful create request returns HTTP 200:
{
"request_id": "task_01KABCDEF1234567890XYZ"
}request_id is an opaque APIOne task identifier. Store it exactly as returned alongside your own user and business-order records. Do not try to parse it or substitute it with another task ID.
Poll a video task
GET https://apione.org/xai/v1/videos/{request_id}
Authorization: Bearer <APIOne API Key>Poll responses
Queued or in progress:
{
"request_id": "task_01KABCDEF1234567890XYZ",
"status": "pending",
"model": "grok-1.5-video"
}Succeeded:
{
"request_id": "task_01KABCDEF1234567890XYZ",
"status": "done",
"model": "grok-1.5-video",
"video": {
"url": "https://files.example.com/videos/result.mp4"
}
}Failed:
{
"request_id": "task_01KABCDEF1234567890XYZ",
"status": "failed",
"model": "grok-1.5-video",
"error": {
"code": "generation_failed",
"message": "Video generation failed"
}
}status | Client action |
|---|---|
pending | The task is unfinished; keep polling. |
done | Read and persist video.url, then stop polling. |
failed | Stop polling and handle error.message. |
GET /xai/v1/videos/generations/{request_id} is a compatibility polling path. New integrations should use the shorter canonical path above.
Recommended polling strategy
After a successful create request, do not submit the task again. Poll with its request_id instead:
- Wait 2 seconds before the first poll, then use exponential delays of 2, 4, and 8 seconds.
- Cap the interval at 8–10 seconds to avoid unnecessary traffic.
- Stop immediately on
doneorfailed. - Set an application-level timeout, such as 10 minutes. A client timeout only stops waiting; it does not necessarily mean the task failed.
Optionally, use the normalized async endpoint GET /v1/api/result?id={request_id}. It reports standard states such as running, succeeded, failed, and violation. If you use the dedicated endpoint above, do not poll both endpoints for the same task.
Reusable JavaScript implementation
The following implementation can be used in server-side Node.js, a Next.js route handler, Cloudflare Workers, or a similar backend environment. Browser clients must call your backend rather than expose the API key.
const APIONE_BASE_URL = 'https://apione.org';
const GROK_VIDEO_MODEL = 'grok-1.5-video';
function validateCreateInput({ prompt, imageUrls, aspectRatio, resolution }) {
if (typeof prompt !== 'string' || !prompt.trim()) {
throw new Error('prompt must be a non-empty string');
}
if (!Array.isArray(imageUrls) || imageUrls.length < 1 || imageUrls.length > 7) {
throw new Error('imageUrls must contain 1 to 7 public image URLs');
}
if (!['2:3', '3:2', '1:1', '16:9', '9:16'].includes(aspectRatio)) {
throw new Error('unsupported aspectRatio');
}
if (!['720p', '1080p'].includes(resolution)) {
throw new Error('resolution must be 720p or 1080p');
}
if (imageUrls.length > 1 && resolution !== '720p') {
throw new Error('multiple reference images support 720p only');
}
for (const value of imageUrls) {
const url = new URL(value);
if (!['http:', 'https:'].includes(url.protocol)) {
throw new Error('every image URL must use HTTP(S)');
}
}
}
async function readJson(response) {
const body = await response.json().catch(() => null);
if (!response.ok) {
const message = body?.message || body?.error?.message || `HTTP ${response.status}`;
const error = new Error(message);
error.status = response.status;
error.code = body?.code || body?.error?.code;
throw error;
}
return body;
}
export async function createGrokVideo({ apiKey, prompt, imageUrls, aspectRatio, resolution }) {
validateCreateInput({ prompt, imageUrls, aspectRatio, resolution });
const imageFields = imageUrls.length === 1
? { image: { url: imageUrls[0] } }
: { reference_images: imageUrls.map((url) => ({ url })) };
const response = await fetch(`${APIONE_BASE_URL}/xai/v1/videos/generations`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: GROK_VIDEO_MODEL,
prompt,
...imageFields,
duration: 15,
aspect_ratio: aspectRatio,
resolution,
}),
});
const result = await readJson(response);
if (!result?.request_id) throw new Error('APIOne did not return request_id');
return result.request_id;
}
export async function waitForGrokVideo({ apiKey, requestId, timeoutMs = 10 * 60 * 1000 }) {
const startedAt = Date.now();
let delayMs = 2_000;
while (Date.now() - startedAt < timeoutMs) {
await new Promise((resolve) => setTimeout(resolve, delayMs));
const response = await fetch(
`${APIONE_BASE_URL}/xai/v1/videos/${encodeURIComponent(requestId)}`,
{ headers: { Authorization: `Bearer ${apiKey}` } },
);
const task = await readJson(response);
if (task.status === 'done') return task.video.url;
if (task.status === 'failed') throw new Error(task.error?.message || 'video generation failed');
if (task.status !== 'pending') throw new Error(`unknown task status: ${task.status}`);
delayMs = Math.min(delayMs * 2, 8_000);
}
throw new Error('timed out waiting for the video task');
}Errors and retry rules
Typical error shape:
{
"code": "invalid_request",
"message": "A required request field is missing",
"data": null
}| Situation | Typical HTTP status | Correct handling |
|---|---|---|
| Invalid fields, image URL, image count, or resolution | 400 | Fix the request; do not retry it unchanged. |
Both image and reference_images sent | 400 | Rebuild the request with exactly one input form. |
Multiple images with 1080p | 400 | Change to 720p or use only one reference image. |
| Missing or invalid API key | 401 | Fix the server-side key configuration. |
| Insufficient quota | 402 | Add quota or use a key with available quota. |
| Rate limited | 429 | Retry with exponential backoff and limit concurrency. |
| Temporary service failure | 5xx | Retry after a short delay; if request_id was returned, poll it instead of creating a duplicate task. |
Do not expose API keys, authorization headers, full task requests, private image URLs, or any server-side diagnostic data in end-user errors or client-visible logs.
AI implementation checklist
- Use only
grok-1.5-video. - Submit JSON only to
POST /xai/v1/videos/generations; poll only throughGET /xai/v1/videos/{request_id}. - Send numeric
duration: 15; useaspect_ratio, notratioorsize. - Supply at least one HTTP(S) image URL: use
image.urlfor one image, or orderedreference_images[].urlfor two to seven images. - Use
resolution: "720p"for multiple images; one image may use720por1080p. - Refer to images in prompt order with
@img1through@img7. - Store and poll only APIOne's
request_id; read the output fromvideo.urlwhen complete. - Keep the API key server-side at all times.
Seedance 2.0 Video Generation
ByteDance Seedance 2.0 full series — supports text-to-video, keyframe video, reference video/audio, and face consistency enhancement.
File Upload
Reference images, videos, and other files must first be converted into a publicly accessible URL before submitting image / video generation tasks.