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 appel | Deux appels | |
|---|---|---|
| Points de terminaison | Un seul point de terminaison d’édition | video-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 personne | Les cibles que vous avez choisies, par leur position |
| Ce que vous envoyez | L’image ou le clip cible et votre image de référence, en une requête | Le clip au premier appel ; les positions choisies, chacune avec ses propres images de référence, au second |
| Ce que vous payez | Une tâche | Deux tâches, facturées séparément |
| Adapté à | Un sujet évident dans le cadre | Plusieurs 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ègle | Pourquoi |
|---|---|
| L’analyse doit appartenir à votre compte et avoir réussi | Une analyse en attente ou en échec n’a rien à désigner |
| Utilisez la même clé API pour les deux appels | Une 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 fois | Hors 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’analyse | En les héritant, vous ne sauriez plus à quel palier tarifaire vous êtes facturé |
| Le clip source doit être le même | Omettez video_url et le clip analysé est repris ; envoyez-le et il doit être identique |
| L’analyse reste utilisable 24 heures après sa fin | Passé 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 :
msg | Ce 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âche | Utilisez 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 family | Pointez 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 / expired | Attendez-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 exploiter | Relancez l’analyse sur le même clip |
That analysis result has expired. Run the analysis step again. | Plus de 24 heures se sont écoulées | Relancez l’analyse |
"resolution" must match the analysis step. | Les deux appels divergent sur un champ qui influe sur le prix | Envoyez 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 clip | Envoyez 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 cible | Donnez 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
Catalogue de modèles et schémas
Lire toolkit, tasks, les prix et le schéma d’entrée propre à chaque point de terminaison
Tâches asynchrones
Créer une tâche, l’interroger et lire sa sortie
Médias
Téléverser un clip ou une photo de référence et obtenir une URI spicy://
Facturation
Réservations, règlement et facturation de la durée par blocs
Guide : générer et retoucher des images
Le comportement commun des points d'accès image — les deux formes de requête, les trois façons d'écrire la taille de sortie, ce qu'une image de référence ajoute à la facture, les poids d'adaptation et la lecture d'un refus.
Intégration en production
Une liste de contrôle avant lancement pour une intégration capable de se rétablir — le bon modèle, des clés et un solde protégés, des coûts confirmés, un suivi durable des tâches, des callbacks fiables et les tests à mener avant la mise en ligne.

