> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apimart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Modèles vidéo officiels Grok

> Générez des vidéos depuis du texte ou des images avec grok-imagine-video et grok-imagine-video-1.5, ou modifiez une vidéo avec le modèle de base.

<Info>
  Cette page concerne les modèles officiels `grok-imagine-video` et `grok-imagine-video-1.5`. Ils sont distincts de `grok-imagine-1.5-video-ext` ; ne mélangez pas noms et paramètres.
</Info>

<Warning>
  N'exposez jamais la clé API dans le navigateur, les variables publiques, LocalStorage, une URL ou les logs. Appelez APIMart via votre backend ou BFF.
</Warning>

<RequestExample>
  ```bash cURL theme={null}
  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
    }'
  ```

  ```javascript JavaScript theme={null}
  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());
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "code": 200,
    "data": [{
      "status": "submitted",
      "task_id": "task_01M09Y4Y101HTFFPV2QPW9DQ3W"
    }]
  }
  ```

  ```json 400 theme={null}
  {
    "error": {
      "message": "Invalid request parameters",
      "type": "invalid_request_error",
      "param": "resolution",
      "code": "invalid_request_error"
    }
  }
  ```
</ResponseExample>

## Vue d'ensemble

Tous les modes utilisent le même endpoint asynchrone :

```http theme={null}
POST https://api.apimart.ai/v1/videos/generations
```

| Champs envoyés               | Mode                           | Modèles                         |
| ---------------------------- | ------------------------------ | ------------------------------- |
| Sans `image_urls` ni `video` | Texte vers vidéo               | Les deux modèles                |
| `image_urls`                 | Images de référence vers vidéo | Les deux modèles                |
| `video`                      | Édition vidéo                  | `grok-imagine-video` uniquement |

Après l'envoi, conservez `data[0].task_id`, puis interrogez :

```http theme={null}
GET https://api.apimart.ai/v1/tasks/{task_id}
```

<Warning>
  N'envoyez pas `X-APIMart-Response-Version` : il active une réponse HTTP `202`. Cette page utilise l'ancien format asynchrone HTTP `200`.
</Warning>

## Capacités

| Capacité                             | `grok-imagine-video` | `grok-imagine-video-1.5` |
| ------------------------------------ | :------------------: | :----------------------: |
| Texte vers vidéo                     |           ✅          |             ✅            |
| Une ou plusieurs images de référence |           ✅          |             ✅            |
| Édition vidéo                        |           ✅          |             ❌            |
| `480p`                               |           ✅          |             ✅            |
| `720p`                               |           ✅          |             ✅            |
| `1080p`                              |           ❌          |             ✅            |
| Durée : 1–15 secondes                |         1–15         |           1–15           |
| Prompt                               |        1–8000        |          1–8000          |

```text theme={null}
duration     = 8
resolution   = 480p
aspect_ratio = auto
```

Le contrat public ne fixe pas de maximum d'images. Conservez un tableau non vide d'URL valides dans l'ordre ; ne réutilisez pas les limites des modèles d'image.

## En-têtes

<ParamField header="Authorization" type="string" required>
  `Bearer <APIMART_API_KEY>`
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Utilisez toujours `application/json`.
</ParamField>

<ParamField header="Accept" type="string">
  `application/json`
</ParamField>

<ParamField header="Idempotency-Key" type="string">
  `Idempotency-Key` est facultatif et fortement recommandé pour les requêtes payantes. Il accepte 1 à 191 caractères ASCII visibles ; UUID recommandé. Un retry réseau réutilise la clé et le corps d'origine. Ne changez pas de clé si le résultat est incertain.

  Utilisez une nouvelle clé par opération logique. Une relance doit reprendre la clé et le corps d'origine.
</ParamField>

## Paramètres

### Champs communs

<ParamField body="model" type="string" required>
  Nom officiel ; l'édition n'accepte que le modèle de base

  * `grok-imagine-video`
  * `grok-imagine-video-1.5`
</ParamField>

<ParamField body="prompt" type="string" required>
  Instruction non vide, 8000 caractères Unicode maximum

  `Array.from(prompt).length`
</ParamField>

<ParamField body="nsfw_check" type="boolean" default={false}>
  Indique si le contenu doit être modéré avant l'envoi de la tâche vidéo.

  * `true`: Utilise `omni-moderation-latest` pour vérifier le prompt et les images d'entrée
  * `false` ou omis: Ne lance aucune modération et n'ajoute ni coût ni latence de contrôle (défaut)
</ParamField>

### Champs de génération

<ParamField body="duration" type="integer" default={8}>
  Génération uniquement ; entier 1–15, défaut 8
</ParamField>

<ParamField body="resolution" type="string" default="480p">
  Base : `480p/720p` ; 1.5 : `480p/720p/1080p` ; défaut `480p`

  * `grok-imagine-video`: `480p`, `720p`
  * `grok-imagine-video-1.5`: `480p`, `720p`, `1080p`
</ParamField>

<ParamField body="aspect_ratio" type="string" default="auto">
  Génération uniquement ; `auto`, `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2` ou `2:3`

  * `auto`
  * `1:1`, `16:9`, `9:16`
  * `4:3`, `3:4`, `3:2`, `2:3`
</ParamField>

<ParamField body="image_urls" type="string[]">
  Tableau facultatif ; chaque élément est une URL HTTPS publique ; omettre s'il est vide

  * Chaque élément doit être une URL HTTPS publique ; les URL relatives, Data URL et Base64 brut ne sont pas acceptés.
  * N'envoyez pas d'alias comme `image`, `images` ou `input_reference`.
  * L'ordre est conservé ; les URL dupliquées occupent plusieurs entrées et peuvent être facturées plusieurs fois.
</ParamField>

### Champs d'édition vidéo

<ParamField body="video" type="object">
  Vidéo source `{url}` en HTTPS public ; modèle de base uniquement

  <Expandable title="URL">
    <ParamField body="url" type="string" required>
      HTTPS
    </ParamField>
  </Expandable>
</ParamField>

L'édition exige `model`, `prompt` et `video`, avec `nsfw_check` en option. N'envoyez pas `duration`, `resolution`, `aspect_ratio` ou `image_urls` ; la plateforme détecte la durée.

## Types de requête TypeScript

Utilisez une union discriminée pour ne pas envoyer les champs de génération à l'édition.

```ts theme={null}
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;
```

## Exemples

<Tabs>
  <Tab title="Texte vers vidéo">
    ```json theme={null}
    {"model":"grok-imagine-video","prompt":"A cinematic aerial shot at sunrise","duration":5,"resolution":"720p","aspect_ratio":"16:9"}
    ```
  </Tab>

  <Tab title="1.5 · 1080p">
    ```json theme={null}
    {"model":"grok-imagine-video-1.5","prompt":"A smooth studio product commercial","duration":5,"resolution":"1080p","aspect_ratio":"16:9"}
    ```
  </Tab>

  <Tab title="Une ou plusieurs images de référence">
    ```json theme={null}
    {
      "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"
      ]
    }
    ```
  </Tab>

  <Tab title="Édition vidéo">
    ```json theme={null}
    {
      "model":"grok-imagine-video",
      "prompt":"Improve motion consistency and apply cinematic color grading",
      "video":{"url":"https://cdn.example.com/source.mp4"}
    }
    ```
  </Tab>
</Tabs>

## Tâches asynchrones

### Création réussie

Une création réussie renvoie HTTP `200`. Enregistrez `data[0].task_id` ; l'envoi ne signifie pas que la vidéo est terminée. Un ID de tâche signifie envoyée, pas terminée.

```json theme={null}
{
  "code":200,
  "data":[{"status":"submitted","task_id":"task_01M09Y4Y101HTFFPV2QPW9DQ3W"}]
}
```

### Interroger la tâche

```http theme={null}
GET https://api.apimart.ai/v1/tasks/{task_id}
Authorization: Bearer <APIMART_API_KEY>
Accept: application/json
```

Interrogez `GET /v1/tasks/{task_id}` toutes les 3–5 secondes. Après rechargement, reprenez avec l'ID enregistré.

| `data.status` | Signification           | Action                            |
| ------------- | ----------------------- | --------------------------------- |
| `pending`     | En file                 | Continuer le polling              |
| `processing`  | Génération              | Afficher la progression           |
| `completed`   | Terminée                | Lire le résultat et arrêter       |
| `failed`      | Échouée et remboursée   | Afficher l'erreur et arrêter      |
| `unknown`     | Temporairement inconnue | Réduire la fréquence et réessayer |

### Réponse terminée

```json theme={null}
{
  "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` est un tableau de chaînes, pas une chaîne unique. Validez chaque valeur comme URL HTTPS. Une validation à l'exécution est recommandée :

```ts theme={null}
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),
        )
      : [],
  );
}
```

Utilisez `expires_at` pour l'expiration. Ne figez pas de durée ; invitez au téléchargement ou à la conservation.

### Réponse en échec

```json theme={null}
{
  "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"}
  }
}
```

<Warning>
  La consultation peut renvoyer HTTP `200` avec `data.status=failed`. Décidez via `data.status` ; une tâche échouée a `cost=0`.
</Warning>

## Catalogue de prix

```http theme={null}
GET https://api.apimart.ai/api/pricing/models/all
```

Lisez `GET /api/pricing/models/all` et cherchez l'`id` dans `data.models.video`. Les prix sont estimés ; le montant final est `data.cost`.

### Prix de la vidéo produite

```json theme={null}
{
  "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}
    ]
  }
}
```

* Les clés tarifaires sont `480P/720P/1080P`, les valeurs de requête en minuscules ; normalisez la casse.
* `default` est une donnée de compatibilité, pas une résolution sélectionnable.
* Utilisez directement `after_discount` sans appliquer une seconde remise.

### Prix des médias d'entrée

```json theme={null}
{"unit":"usd_per_image","original_price":0.002,"after_discount":0.0016}
```

```json theme={null}
{"unit":"usd_per_second","original_price":0.01,"after_discount":0.008}
```

Le prix vidéo est un objet scalaire. N'exigez pas `items`, `billing_mode` ou `max_billable_seconds`. La version 1.5 n'a pas de prix vidéo entrant.

### Formules d'estimation

```text theme={null}
Génération = prix de sortie/seconde × durée + prix par image × nombre
Édition = prix 720P/seconde × secondes source + prix d'entrée vidéo × secondes source
```

Les tarifs propres à l'utilisateur et l'arrondi peuvent varier. Le montant final est toujours `data.cost`.

## Règles frontend

### Changement de modèle

* Base affiche `480p/720p` ; 1.5 ajoute `1080p`.
* Passer de 1.5 `1080p` à Base revient à `480p`.
* L'édition fixe `grok-imagine-video`.

### Changement de mode

| Mode                | Contrôles affichés                                   | Champs envoyés                      | À effacer                                     |
| ------------------- | ---------------------------------------------------- | ----------------------------------- | --------------------------------------------- |
| Génération          | `prompt/duration/resolution/aspect_ratio/nsfw_check` | Champs de génération                | `image_urls/video`                            |
| Images de référence | Champs de génération + `image_urls`                  | Champs de génération + `image_urls` | `video`                                       |
| Édition vidéo       | `prompt/video/nsfw_check`                            | `model/prompt/video/nsfw_check`     | `duration/resolution/aspect_ratio/image_urls` |

`nsfw_check` est facultatif dans tous les modes. Envoyez `true` si la modération est activée ; sinon omettez-le ou envoyez `false`.

Désactivez le bouton si l'une de ces conditions est vraie :

* Le mode texte omet `image_urls` et `video`.
* Le mode référence envoie `image_urls` et omet `video`.
* L'édition efface les champs de génération.
* Désactiver si prompt, durée, résolution ou URL est invalide, pendant l'upload ou en cas de doublon.
* Prompt ≤8000 Unicode et durée entière 1–15.
* URL HTTPS publiques uniquement ; omettre `image_urls` vide.

## Erreurs courantes

| HTTP / Statut | Cause                                  | Traitement                              |
| ------------- | -------------------------------------- | --------------------------------------- |
| `400`         | Paramètres, prompt ou enum invalide    | Afficher le message et le champ         |
| `401`         | Clé absente ou invalide                | Ne pas réessayer ; vérifier le serveur  |
| `402`         | Solde insuffisant                      | Demander un rechargement                |
| `403`         | Permission absente                     | Ne pas réessayer automatiquement        |
| `409`         | Conflit idempotent ou requête en cours | Conserver la clé et réessayer plus tard |
| `429`         | Limitation                             | Respecter `Retry-After`                 |
| `500/502/503` | Panne temporaire                       | Retries limités avec la clé d'origine   |
| `failed`      | Tâche asynchrone échouée               | Arrêter le polling ; coût nul           |

## Liste de contrôle

* Clé API uniquement dans backend ou BFF.
* Ne pas mélanger les modèles officiels avec `grok-imagine-1.5-video-ext`.
* Prompt ≤8000 Unicode et durée entière 1–15.
* URL HTTPS publiques uniquement ; omettre `image_urls` vide.
* Pour l'édition, envoyez uniquement `model/prompt/video` avec `nsfw_check` facultatif et utilisez le modèle de base.
* Lire `data[0].task_id` et l'état final via `data.status`.
* Lire `result.videos[].url[]` et respecter `expires_at`.
* Afficher le catalogue et utiliser le `data.cost` final.
