spicyapiDocumentation
Guides
Contenu principal

Remplacement de personne

Une seule boîte à outils, deux formes. Les modèles à un appel remplacent un visage ou une tête en une requête ; les modèles à deux appels analysent d’abord le clip pour que vous choisissiez qui change.

Certains modèles ne créent pas une scène : ils remplacent une personne. Un visage repris d’une photo et posé sur une autre, une tête entière, ou toute une silhouette dans un clip vidéo. Le catalogue les regroupe sous une même étiquette, toolkit: "subject-swap", et ils existent en deux formes — un appel, ou deux appels avec un choix entre les deux.

Rien d’autre ne les distingue. Ce sont des tâches asynchrones ordinaires, leurs paramètres viennent de leur propre inputSchema et leurs résultats arrivent dans output.assets[].

toolkit vous dit que le modèle en fait partie

toolkit est une étiquette de classification posée sur une entrée du catalogue, présente aussi bien dans la réponse authentifiée de GET /api/v1/models que dans le catalogue public. Elle n’a aujourd’hui qu’une valeur, subject-swap, et le champ est absent sur les modèles qui n’appartiennent à aucune boîte à outils : testez donc la valeur, jamais la présence de la clé.

Le champ est purement informatif. Il ne décide ni de la clé autorisée à appeler un modèle, ni du prix de la requête, ni de ce que le modèle accepte. Traitez une valeur inconnue comme une entrée badges inconnue : ignorez-la.

curl -s "https://api.spicyapi.ai/api/v1/models?modality=video" \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  | jq -r '.data.items[]
      | select(.toolkit == "subject-swap" and .enabled and .available)
      | [.model, (.tasks | join(",")), .family] | @tsv'

Chaque ligne correspond à un point de terminaison appelable. C’est la colonne tasks qui détermine tout le reste de cette page : une family qui possède un point de terminaison video-analyze est une family à deux appels, et son point de terminaison video-edit associé ne s’appelle pas seul.

Quelle forme ai-je sous les yeux ?

Un appelDeux appels
Points de terminaisonUn seul point de terminaison d’éditionvideo-analyze, puis video-edit, dans la même family
Qui est remplacéCe que le modèle trouve. La requête n’a aucun champ pour désigner une personneLes cibles que vous avez choisies, par leur position
Ce que vous envoyezL’image ou le clip cible et votre image de référence, en une requêteLe clip au premier appel ; les positions choisies, chacune avec ses propres images de référence, au second
Ce que vous payezUne tâcheDeux tâches, facturées séparément
Adapté àUn sujet évident dans le cadrePlusieurs personnes dans le cadre, ou lorsqu’un mauvais choix coûte cher

Les deux formes sont en async: true : vous créez une tâche, puis récupérez le résultat par interrogation ou par webhook.

Un appel

Rien ne change par rapport à n’importe quel autre modèle d’édition. Lisez l’inputSchema du point de terminaison, remplissez-le, créez la tâche :

const created = await fetch('https://api.spicyapi.ai/api/v1/jobs/createTask', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.SPICY_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'order-8814-swap-1',
  },
  body: JSON.stringify({
    model: swap.model, // une entrée de la requête catalogue ci-dessus
    input: {
      image_url: 'spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3',
      reference_image_url: 'spicy://f/fil_01k3m8xb2c3d4e5f6g7h8j9k0m',
    },
  }),
});

Les noms de champs varient d’un point de terminaison à l’autre : certains acceptent un prompt, un seed ou un format de sortie, d’autres rien d’autre que les deux images. Le schéma fait foi ; ne transposez pas les noms de champs d’un point de terminaison à un autre. Une référence peut être une URL HTTPS publique, une petite image en Base64 directement dans la requête, ou une URI spicy:// téléversée ; voir Médias.

Deux appels

Le premier appel examine votre clip et rapporte ce qu’il y a trouvé, sous la forme d’un ensemble ordonné d’images fixes de prévisualisation. Vous décidez lesquelles remplacer. Le second appel fait le travail.

votre clip ──▶ video-analyze ──▶ output.assets[0], [1], [2] …   (une image par cible)
                                          │
            vous choisissez les positions │ 0 et 2
                                          ▼
          video-edit (analysis_task_id + swap_targets) ──▶ le clip terminé

Pourquoi ne pas tout faire en un appel ? Parce que dans une scène à quatre personnes, un appel unique devrait deviner laquelle vous visiez, et une mauvaise devinette donne une vidéo terminée et facturée où c’est la mauvaise personne qui a été remplacée. Scinder le travail rend le choix explicite et vérifiable.

Analyser le clip

Envoyez le clip au point de terminaison video-analyze. Renseignez explicitement resolution si le point de terminaison possède ce champ : le second appel devra tourner à la même valeur, choisissez donc dès maintenant la résolution du clip final. Sur ces points de terminaison, le prix de l’analyse n’en dépend pas.

const analysis = await createTask({
  model: analyze.model, // l’entrée dont les tasks contiennent "video-analyze"
  input: {
    video_url: 'spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3',
    resolution: '720p',
  },
});
const analysisTaskId = analysis.taskId;

Lire ce qu’il a trouvé

Attendez la tâche comme d’habitude. Une analyse réussie renvoie une image fixe de prévisualisation par cible suivie, dans output.assets[] :

{
  "state": "succeeded",
  "output": {
    "assets": [
      { "url": "https://…", "expiresAt": "2026-09-20T09:32:11Z", "mime": "image/jpeg" },
      { "url": "https://…", "expiresAt": "2026-09-20T09:32:11Z", "mime": "image/jpeg" }
    ]
  },
  "settled": true
}

L’ordre de ce tableau est le contrat. L’indice d’un élément est l’object_index que vous renverrez au second appel : la première image est 0, la deuxième 1, et ainsi de suite. Montrez les images à la personne qui choisit, ou faites la correspondance vous-même ; dans les deux cas, renvoyez des positions issues de ce même tableau.

Une analyse qui ne trouve rien à suivre ne réussit pas avec un tableau vide : elle se termine en failed avec errorCode: "generation_failed", et la réservation est libérée intégralement. Une analyse en succeeded comporte donc toujours au moins une image, et la seule chose que votre code ait à gérer est le choix.

Remplacer les cibles choisies

Envoyez l’identifiant de la tâche d’analyse et une entrée swap_targets par cible à remplacer. Les cibles que vous omettez restent intactes.

const swapped = await createTask({
  model: edit.model, // l’entrée "video-edit" de la même family
  input: {
    analysis_task_id: analysisTaskId,
    resolution: '720p', // la valeur à laquelle l’analyse a tourné
    swap_targets: [
      { object_index: 0, image_urls: ['spicy://f/fil_01k3m8xb2c3d4e5f6g7h8j9k0m'] },
    ],
  },
});

Le résultat est un clip dans output.assets[], comme pour toute autre tâche vidéo.

Ce que le second appel vérifie avant d’accepter

Ces vérifications ont lieu à la création : une requête refusée ne crée rien et ne réserve rien.

RèglePourquoi
L’analyse doit appartenir à votre compte et avoir réussiUne analyse en attente ou en échec n’a rien à désigner
Utilisez la même clé API pour les deux appelsUne analyse ne se relit qu’avec la clé qui l’a créée ; avec une autre, vous ne voyez même pas les images parmi lesquelles vous choisissez
Chaque object_index doit être dans les bornes et n’apparaître qu’une foisHors bornes, c’est la mauvaise personne qui serait remplacée sans un mot ; en double, la même le serait deux fois. Les deux iraient au bout et seraient facturées normalement
Les champs qui influent sur le prix, comme resolution, doivent correspondre à l’analyseEn les héritant, vous ne sauriez plus à quel palier tarifaire vous êtes facturé
Le clip source doit être le mêmeOmettez video_url et le clip analysé est repris ; envoyez-le et il doit être identique
L’analyse reste utilisable 24 heures après sa finPassé ce délai, les données de suivi qui la sous-tendent ont disparu — et souvent le clip source aussi

Renseignez `resolution` dans les deux appels, ou lisez la valeur par défaut avec attention

Les champs omis prennent la valeur default de leur schéma avant que les deux requêtes soient comparées. Une analyse laissée par défaut et un remplacement laissé par défaut correspondent donc bien — mais une analyse dont vous avez fixé la valeur et un remplacement laissé vide, non, et l’écart est refusé au lieu d’être corrigé en silence. Envoyer la valeur explicitement dans les deux appels supprime la question.

Quand le second appel est refusé

Tous ces cas sont des HTTP 400 avec le code métier code: 400. Le champ msg vaut l’une des valeurs suivantes :

msgCe qui s’est passéQuoi faire
The referenced analysis task was not found.Aucune tâche de ce genre sur ce compte, ou analysis_task_id n’est pas un identifiant de tâcheUtilisez le taskId renvoyé par votre propre analyse. Une tâche appartenant à un autre compte est signalée de la même façon, volontairement
The referenced task belongs to a different model.L’identifiant pointe vers une autre tâche — souvent la tâche de remplacement elle-même, ou l’analyse d’une autre familyPointez vers la tâche video-analyze de la même family
The referenced analysis did not complete successfully.L’analyse tourne encore, ou s’est terminée en failed / expiredAttendez-la, ou relancez-la
That analysis has no reusable result. Run the analysis step again.L’analyse a réussi mais n’a rien laissé que le remplacement puisse exploiterRelancez l’analyse sur le même clip
That analysis result has expired. Run the analysis step again.Plus de 24 heures se sont écouléesRelancez l’analyse
"resolution" must match the analysis step.Les deux appels divergent sur un champ qui influe sur le prixEnvoyez la valeur de l’analyse, ou lancez une nouvelle analyse à la valeur souhaitée
"video_url" must match the analysis step.Vous avez envoyé un autre clipEnvoyez la même URI, ou omettez le champ
That analysis found N target(s); "object_index" is out of range.Une position dépasse ce que l’analyse a renvoyéLes positions partent de zéro et s’arrêtent à assets.length - 1
"object_index" is listed more than once.Deux entrées désignent la même cibleDonnez une seule entrée par cible, avec toutes ses images de référence dans l’image_urls de cette entrée

Ce que coûtent les deux appels

L’analyse et le remplacement sont deux tâches distinctes à deux prix distincts, réservées et réglées chacune de leur côté. Un second appel refusé ne coûte rien ; une analyse réussie est facturée même si vous renoncez ensuite à remplacer quoi que ce soit.

  • L’analyse est en général tarifée per_request : une facturation par appel, quelle que soit la durée du clip et quel que soit le nombre de cibles trouvées.
  • Le remplacement est en général tarifé per_second, par blocs entiers, et la durée vient du clip et non d’un champ que vous renseignez. Le montant annoncé avant l’envoi correspond donc au clip le plus long que le point de terminaison accepte : le montant annoncé est le montant réservé, la facturation finale se calcule sur la durée arrondie et ne dépasse jamais la réservation, et la différence revient sur votre solde au règlement de la tâche. Voir ici une réservation supérieure à la facturation finale est normal. Lisez Facturation avant d’établir un budget.

Lisez les deux prix dans le catalogue au lieu de les supposer, et demandez un devis pour chaque appel si le montant exact compte.

Étapes suivantes

Sur cette page