curl --request POST \
--url https://api.apimart.ai/v1/videos/generations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Idempotency-Key: 7d0141e4-a19a-4650-a717-dab777b3a330' \
--data '{
"model": "grok-imagine-video",
"prompt": "A cinematic aerial shot of a coastal city at sunrise",
"duration": 5,
"resolution": "720p",
"aspect_ratio": "16:9",
"nsfw_check": true
}'
const response = await fetch("https://api.apimart.ai/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer <token>",
"Content-Type": "application/json",
Accept: "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
model: "grok-imagine-video",
prompt: "Improve motion consistency and apply cinematic color grading",
video: { url: "https://cdn.example.com/source-video.mp4" },
}),
});
console.log(response.status, await response.json());
{
"code": 200,
"data": [{
"status": "submitted",
"task_id": "task_01M09Y4Y101HTFFPV2QPW9DQ3W"
}]
}
{
"error": {
"message": "Invalid request parameters",
"type": "invalid_request_error",
"param": "resolution",
"code": "invalid_request_error"
}
}
Grok Imagine
Grok Official Video Models
Generate videos from text or reference images with grok-imagine-video and grok-imagine-video-1.5, or edit a source video with the base model.
POST
/
v1
/
videos
/
generations
curl --request POST \
--url https://api.apimart.ai/v1/videos/generations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Idempotency-Key: 7d0141e4-a19a-4650-a717-dab777b3a330' \
--data '{
"model": "grok-imagine-video",
"prompt": "A cinematic aerial shot of a coastal city at sunrise",
"duration": 5,
"resolution": "720p",
"aspect_ratio": "16:9",
"nsfw_check": true
}'
const response = await fetch("https://api.apimart.ai/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer <token>",
"Content-Type": "application/json",
Accept: "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
model: "grok-imagine-video",
prompt: "Improve motion consistency and apply cinematic color grading",
video: { url: "https://cdn.example.com/source-video.mp4" },
}),
});
console.log(response.status, await response.json());
{
"code": 200,
"data": [{
"status": "submitted",
"task_id": "task_01M09Y4Y101HTFFPV2QPW9DQ3W"
}]
}
{
"error": {
"message": "Invalid request parameters",
"type": "invalid_request_error",
"param": "resolution",
"code": "invalid_request_error"
}
}
This page covers the official models
grok-imagine-video and grok-imagine-video-1.5. They are separate from grok-imagine-1.5-video-ext on the existing generation page; do not mix their model names or parameters.Never expose an API key in a browser bundle, public environment variable, LocalStorage, URL, or frontend logs. Call APIMart through your backend or BFF.
curl --request POST \
--url https://api.apimart.ai/v1/videos/generations \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Idempotency-Key: 7d0141e4-a19a-4650-a717-dab777b3a330' \
--data '{
"model": "grok-imagine-video",
"prompt": "A cinematic aerial shot of a coastal city at sunrise",
"duration": 5,
"resolution": "720p",
"aspect_ratio": "16:9",
"nsfw_check": true
}'
const response = await fetch("https://api.apimart.ai/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer <token>",
"Content-Type": "application/json",
Accept: "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
model: "grok-imagine-video",
prompt: "Improve motion consistency and apply cinematic color grading",
video: { url: "https://cdn.example.com/source-video.mp4" },
}),
});
console.log(response.status, await response.json());
{
"code": 200,
"data": [{
"status": "submitted",
"task_id": "task_01M09Y4Y101HTFFPV2QPW9DQ3W"
}]
}
{
"error": {
"message": "Invalid request parameters",
"type": "invalid_request_error",
"param": "resolution",
"code": "invalid_request_error"
}
}
Integration overview
All modes use the same asynchronous endpoint:POST https://api.apimart.ai/v1/videos/generations
| Request fields | Mode | Models |
|---|---|---|
No image_urls or video | Text to video | Both models |
image_urls | Reference images to video | Both models |
video | Video editing | grok-imagine-video only |
data[0].task_id, then poll:
GET https://api.apimart.ai/v1/tasks/{task_id}
Do not send
X-APIMart-Response-Version. It switches to an HTTP 202 response schema; this page uses the legacy HTTP 200 asynchronous task response.Model capabilities
| Capability | grok-imagine-video | grok-imagine-video-1.5 |
|---|---|---|
| Text to video | ✅ | ✅ |
| Single or multiple reference images | ✅ | ✅ |
| Video editing | ✅ | ❌ |
480p | ✅ | ✅ |
720p | ✅ | ✅ |
1080p | ❌ | ✅ |
| Duration: 1–15 seconds | 1–15 | 1–15 |
| Prompt | 1–8000 | 1–8000 |
duration = 8
resolution = 480p
aspect_ratio = auto
Request headers
string
required
Bearer <APIMART_API_KEY>string
required
Always use
application/json.string
application/jsonstring
Idempotency-Key is optional and strongly recommended for paid generation and editing. It accepts 1–191 visible ASCII characters; UUID is recommended. Reuse the original key and identical body for a network retry. Do not switch keys when the result is uncertain.Use a new key for each new logical operation. A retry of the same operation must reuse the original key and identical body.Request parameters
Common fields
string
required
Official model name; video editing supports the base model only
grok-imagine-videogrok-imagine-video-1.5
string
required
Non-empty instruction, at most 8000 Unicode characters
Array.from(prompt).lengthboolean
default:false
Whether to perform content moderation before submitting the video task.
true: Useomni-moderation-latestto review the prompt and input imagesfalseor omitted: Do not send a moderation request, adding no moderation cost or latency (default)
Generation fields
integer
default:8
Generation only; integer from 1 to 15, default 8
string
default:"480p"
Base:
480p/720p; 1.5: 480p/720p/1080p; default 480pgrok-imagine-video:480p,720pgrok-imagine-video-1.5:480p,720p,1080p
string
default:"auto"
Generation only;
auto, 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, or 2:3auto1:1,16:9,9:164:3,3:4,3:2,2:3
string[]
Optional reference-image array; every item must be a public HTTPS URL; omit instead of sending an empty array
- Every item must be a publicly accessible HTTPS URL; relative URLs, Data URLs, and raw Base64 are not supported.
- Do not send aliases such as
image,images, orinput_reference. - Array order is preserved; duplicate URLs occupy multiple input slots and may be billed more than once.
Video-edit fields
A video-edit request requiresmodel, prompt, and video, and may optionally include nsfw_check. Do not send duration, resolution, aspect_ratio, or image_urls; the platform detects the source duration.
TypeScript request types
Use a discriminated union so generation-only fields cannot be sent to video editing.type GrokVideoModel =
| "grok-imagine-video"
| "grok-imagine-video-1.5";
type GrokVideoResolution = "480p" | "720p" | "1080p";
type GrokVideoAspectRatio =
| "auto"
| "1:1"
| "16:9"
| "9:16"
| "4:3"
| "3:4"
| "3:2"
| "2:3";
interface GrokVideoGenerateRequest {
model: GrokVideoModel;
prompt: string;
nsfw_check?: boolean;
duration?: number;
resolution?: GrokVideoResolution;
aspect_ratio?: GrokVideoAspectRatio;
image_urls?: string[];
}
interface GrokVideoEditRequest {
model: "grok-imagine-video";
prompt: string;
nsfw_check?: boolean;
video: { url: string };
}
type GrokVideoRequest =
| GrokVideoGenerateRequest
| GrokVideoEditRequest;
Request examples
- Text to video
- 1.5 · 1080p
- Single or multiple reference images
- Video editing
{"model":"grok-imagine-video","prompt":"A cinematic aerial shot at sunrise","duration":5,"resolution":"720p","aspect_ratio":"16:9"}
{"model":"grok-imagine-video-1.5","prompt":"A smooth studio product commercial","duration":5,"resolution":"1080p","aspect_ratio":"16:9"}
{
"model":"grok-imagine-video-1.5",
"prompt":"Use the first image as subject and the second as style",
"duration":5,
"resolution":"720p",
"aspect_ratio":"16:9",
"image_urls":[
"https://cdn.example.com/subject.jpg",
"https://cdn.example.com/style.jpg"
]
}
{
"model":"grok-imagine-video",
"prompt":"Improve motion consistency and apply cinematic color grading",
"video":{"url":"https://cdn.example.com/source.mp4"}
}
Asynchronous tasks
Create success
A successful create request returns HTTP200. Save data[0].task_id; submission does not mean the video is complete. A task ID means submitted, not completed.
{
"code":200,
"data":[{"status":"submitted","task_id":"task_01M09Y4Y101HTFFPV2QPW9DQ3W"}]
}
Query a task
GET https://api.apimart.ai/v1/tasks/{task_id}
Authorization: Bearer <APIMART_API_KEY>
Accept: application/json
GET /v1/tasks/{task_id} every 3–5 seconds. Resume polling with the saved task ID after a page refresh.
data.status | Meaning | Frontend action |
|---|---|---|
pending | Queued | Continue polling |
processing | Generating | Show progress and continue |
completed | Completed | Read the result and stop |
failed | Failed and refunded | Show the error and stop |
unknown | Temporarily unknown | Reduce polling frequency and retry later |
Completed response
{
"code":200,
"data":{
"id":"task_xxx",
"status":"completed",
"progress":100,
"created":1787040038,
"completed":1787040081,
"actual_time":43,
"estimated_time":100,
"cost":0.072,
"credits_cost":0.72,
"result":{"videos":[{"url":["https://cdn.example.com/result.mp4"],"expires_at":1787126481}]}
}
}
result.videos[0].url is an array of strings, not a single string. Validate every value as an HTTPS URL before display. Runtime validation is recommended:
function extractVideoURLs(payload: unknown): string[] {
const groups = (payload as any)?.data?.result?.videos;
if (!Array.isArray(groups)) return [];
return groups.flatMap((group: any) =>
Array.isArray(group?.url)
? group.url.filter(
(url: unknown): url is string =>
typeof url === "string" && /^https:///i.test(url),
)
: [],
);
}
expires_at as the source of truth for URL expiry. Do not hard-code a lifetime; prompt users to download or persist the result.
Failed response
{
"code":200,
"data":{
"id":"task_xxx",
"status":"failed",
"progress":100,
"cost":0,
"credits_cost":0,
"error":{"message":"Task failed.","type":"task_failed","param":"","code":"task_failed"}
}
}
A task query can return HTTP
200 while data.status is failed. Determine success from data.status; failed tasks have cost=0.Pricing catalog
GET https://api.apimart.ai/api/pricing/models/all
GET /api/pricing/models/all and find the model by id in data.models.video. Prices are estimates; the authoritative final amount is data.cost from the task response.
Output-video pricing
{
"fixed_prices":{
"unit":"usd_per_second",
"dimension":"resolution",
"items":[
{"key":"480P","original_price":0.05,"after_discount":0.04},
{"key":"720P","original_price":0.07,"after_discount":0.056}
]
}
}
- Pricing keys use uppercase
480P/720P/1080P, while request values use lowercase; normalize case when looking up prices. defaultis compatibility metadata, not a selectable resolution.- Use
after_discountdirectly; do not apply the discount again.
Input-material pricing
{"unit":"usd_per_image","original_price":0.002,"after_discount":0.0016}
{"unit":"usd_per_second","original_price":0.01,"after_discount":0.008}
items, billing_mode, or max_billable_seconds. Model 1.5 has no video-input price because it cannot edit video.
Estimate formulas
Generation estimate = discounted output price per second × duration + discounted image price × image count
Edit estimate = discounted 720P output price per second × source seconds + discounted video-input price × source seconds
data.cost.
Frontend rules
Model switching
- Base model shows only
480p/720p; 1.5 also shows1080p. - Switching from 1.5
1080pto base must fall back to480p. - Video-edit mode fixes the model to
grok-imagine-video.
Mode switching
| Mode | Visible controls | Submitted fields | Must clear |
|---|---|---|---|
| Generation | prompt/duration/resolution/aspect_ratio/nsfw_check | Generation fields | image_urls/video |
| Reference images | Generation fields + image_urls | Generation fields + image_urls | video |
| Video editing | prompt/video/nsfw_check | model/prompt/video/nsfw_check | duration/resolution/aspect_ratio/image_urls |
nsfw_check is optional in every mode. Send true when moderation is enabled; omit it or send false when disabled.
Disable the run button when any of these conditions applies:
- Text mode omits
image_urlsandvideo. - Reference mode sends
image_urlsand omitsvideo. - Video-edit mode clears all generation-only fields.
- Disable submission for an empty or over-limit prompt, invalid duration, unsupported resolution, invalid material URL, active upload, or duplicate submission.
- Limit prompts to 8000 Unicode characters and duration to integers from 1 to 15.
- Use public HTTPS URLs only; omit empty
image_urls.
Common errors
| HTTP / Status | Common cause | Handling |
|---|---|---|
400 | Invalid parameters, prompt limit, or unsupported enum | Show the server message and identify the field |
401 | Missing or invalid API key | Do not retry; check server configuration |
402 | Insufficient balance | Prompt the user to top up |
403 | Missing model permission | Do not retry automatically |
409 | Idempotency conflict or original request still running | Keep the original key and retry the same request later |
429 | Rate limit | Honor Retry-After or use exponential backoff |
500/502/503 | Temporary service failure | Retry a bounded number of times with the original key |
failed | Asynchronous task failure | Stop polling, show the error; cost is zero |
Frontend checklist
- Keep the API key only in the backend or BFF.
- Do not mix official model names with
grok-imagine-1.5-video-ext. - Limit prompts to 8000 Unicode characters and duration to integers from 1 to 15.
- Use public HTTPS URLs only; omit empty
image_urls. - Send only
model/prompt/videoplus optionalnsfw_checkfor video editing, and use the base model. - Read
data[0].task_idon submit and determine the terminal state fromdata.status. - Read output from
result.videos[].url[]and respectexpires_at. - Use catalog prices for display and task
data.costfor the final amount.