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.
Ce guide porte sur la famille des points d'accès image, et non sur un modèle en particulier. Il décrit ce qui ne change jamais d'un point d'accès à l'autre et, plus utile encore, les endroits où ils divergent au point qu'un corps de requête écrit pour l'un ne passera pas sur le suivant.
Vous n'y trouverez volontairement aucun tableau de paramètres. Les paramètres ont une source unique : le inputSchema que le catalogue en direct renvoie pour le modèle exact que vous appelez. Cette page explique comment lire ce schéma et ce qu'il ne dit pas à voix haute.
| Si vous cherchez | Lisez plutôt |
|---|---|
| Votre première image, de la clé au fichier, d'un bout à l'autre | Démarrage rapide |
| Le même parcours pour la vidéo, étape par étape | Tutoriel : transformer une photo en vidéo |
| Tous les champs d'un modèle précis | Son inputSchema — voir Catalogue de modèles et schémas |
| Placer un visage ou une tête d'une photo dans une autre | Échange de sujet |
MODEL_ID_FROM_CATALOG est un espace réservé
Tous les exemples ci-dessous écrivent MODEL_ID_FROM_CATALOG là où va un vrai identifiant de modèle. Prenez cet identifiant dans le catalogue en direct authentifié — GET /api/v1/models?modality=image — et exigez enabled: true et available: true avant toute dépense. Lire le catalogue est gratuit. Envoyer l'espace réservé lui-même ne renvoie qu'une erreur « not found ».
Deux formes de requête
Le travail sur image arrive sous forme de deux tâches, et le catalogue les sépare :
- Les points d'accès de génération se terminent par
/text-to-image. Vous envoyez des mots, vous obtenez une image. - Les points d'accès de retouche se terminent par
/edit. Vous envoyez en plus au moins une image.
La retouche possède deux orthographes admises, et il vaut mieux connaître les deux avant de filtrer le catalogue : les points d'accès sont publiés avec le suffixe edit, tandis que le tableau tasks les classe comme image-to-image. Le paramètre de requête task accepte l'un comme l'autre. Voir Catalogue de modèles et schémas.
Les deux formes utilisent la même enveloppe de création, le même cycle de vie de tâche et le même format de résultat que tout autre modèle média. Ce qui change, c'est input.
prompt est obligatoire dans les deux
On se dit volontiers qu'un point d'accès de retouche peut travailler à partir de la seule image, et que prompt y serait donc facultatif. Sur les points d'accès qui acceptent un prompt, ce n'est pas le cas : prompt figure dans la liste required racine des deux formes, et l'omettre est refusé avant toute facturation.
{
"model": "MODEL_ID_FROM_CATALOG",
"input": {
"image_urls": ["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]
}
}prompt: is requiredL'image dit d'où vous partez ; le prompt dit ce qu'il faut en faire. Un point d'accès de retouche qui n'a reçu aucune consigne n'a rien sur quoi agir.
Il existe une exception, et c'est une famille entière plutôt qu'un cas isolé : certains points d'accès de retouche n'acceptent aucun prompt et ne prennent que des images, parce que l'opération est fixée par le point d'accès lui-même. Ils appartiennent à la boîte à outils d'échange de sujet et portent un champ toolkit dans le catalogue, ce qui permet de les repérer ; voir Échange de sujet. Pour ceux-là, prompt: is not a supported field est la bonne réponse, pas un défaut.
Le champ d'image d'entrée porte trois noms
Lequel est utilisé fait partie du schéma du point d'accès et ne se déduit pas du type de tâche :
| Champ | Forme | Ce que cela signifie d'ordinaire |
|---|---|---|
image_url | une référence d'image | Il y a exactement une image de sujet, et le point d'accès n'a pas de notion de seconde |
image_urls | tableau de références d'image | Les images sont la matière retouchée ou combinée, dans l'ordre auquel la consigne se réfère |
reference_image_urls | tableau de références d'image | Les images orientent une génération qui serait sinon uniquement textuelle — ce sont des repères, pas le sujet |
C'est reference_image_urls qui surprend : il apparaît sur des points d'accès de génération, donc un point d'accès /text-to-image peut tout à fait accepter des images. Ne supposez pas que « text-to-image » signifie « aucune image dans la requête ».
Une référence d'image est une URL HTTPS publique, une URI spicy:// validée après un téléversement, ou une petite URI data:image/…;base64,…. Pour les téléversements, voir Médias ; pour les noms de champs canoniques, le contrat Spicy Schema.
Les limites des tableaux varient bien plus qu'on ne le croit
Les tableaux ci-dessus portent minItems et maxItems, le serveur applique les deux, et les valeurs diffèrent selon le point d'accès — aujourd'hui, cela va d'un tableau qui n'accepte qu'une seule entrée à des tableaux qui en acceptent dix. Un point d'accès de la même famille, avec le même nom de champ, peut avoir un plafond différent.
image_urls: must contain at least 1 items
image_urls: must contain at most 3 itemsLisez minItems et maxItems dans le schéma du modèle que vous appelez. Un tableau de paramètres qui affiche un tableau comme un simple array, sans forme d'entrée ni limite de longueur, est précisément la ligne sur laquelle un lecteur ne peut rien faire — et c'est exactement pour cela que ce site n'en publie aucun.
La taille de sortie s'écrit de trois façons
C'est la première chose sur laquelle on bute en dirigeant un corps de requête existant vers un second point d'accès.
Trois orthographes coexistent dans le catalogue image, et un point d'accès n'en accepte qu'une seule :
| Orthographe | Champs | Remarques |
|---|---|---|
| Séparée | aspect_ratio et resolution | La forme du cadre et le palier de pixels se choisissent séparément |
| Préréglage | size | Une valeur d'énumération fixe les deux d'un coup |
| Explicite | width et height | Pixels entiers, chacun avec ses propres minimum et maximum |
L'orthographe séparée est la plus répandue sur les points d'accès récents, et la plus utile des trois : aspect_ratio compose le cadre avant la génération plutôt que de recadrer après, ce qui permet de changer la forme sans toucher au budget de pixels.
Un champ de la mauvaise orthographe est refusé, pas ignoré
Tout inputSchema d'image pose additionalProperties: false. Un champ que le point d'accès ne liste pas est rejeté : un corps de requête repris d'un autre point d'accès échoue franchement au lieu de retomber discrètement sur une valeur par défaut.
L'échec se produit dans les deux sens, et le message nomme le champ :
size: is not a supported field
aspect_ratio: is not a supported fieldRien n'est facturé dans un cas comme dans l'autre. Le coût n'est pas financier, il est dans l'hypothèse : vous ne pouvez pas écrire un seul objet input et le diriger vers plusieurs points d'accès image, et il n'existe pas d'orthographe « de compatibilité » que tous accepteraient.
Laquelle sélectionne un palier tarifaire
Sur un point d'accès à orthographe séparée, les deux champs jouent en général des rôles distincts dans la facture :
resolutionsélectionne normalement un palier tarifaire. Il apparaît dans le devis sousvariant, et le déplacer déplace le montant.aspect_ratio, normalement non. Recadrer n'est pas redimensionner.
Le « normalement » de ces deux phrases travaille vraiment. La réponse lisible par une machine se trouve sur le champ lui-même : x-ui.affects_price: true et, lorsque le point d'accès le déclare, l'appartenance à x-pricing.variantFields. Vérifiez le schéma plutôt que l'habitude. Voir Lire les tarifs et la référence des annotations dans le contrat Spicy Schema.
Les valeurs autorisées sont elles aussi propres au point d'accès. Un refus les énumère, ce qui est une façon gratuite de les voir :
aspect_ratio: must be one of: "1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "21:9"Pratique en exploration, mais cela ne remplace pas la lecture d'enum, car le schéma porte aussi le default appliqué quand vous omettez le champ.
Ce qu'une image de référence ajoute à la facture
C'est le poste discret, et il mérite sa propre section parce que rien dans la réponse ne le signale.
L'arithmétique habituelle d'un modèle média est prix × quantité, la quantité étant la valeur de quantityField dans votre entrée, ou une unité si quantityField est vide. Sur les points d'accès image, quantityField vaut le plus souvent num_outputs, et il se comporte exactement comme on l'attend : demandez quatre images et le devis affiche quantity à 4. Cette dimension est honnête — on la voit bouger.
Certains points d'accès ajoutent par-dessus un montant distinct pour chaque image d'entrée, et celui-là, on ne le voit pas bouger. Demandez deux devis pour le même point d'accès, l'un avec une seule référence et l'autre avec un tableau plein, et vous obtenez :
- la même
unit; - la même
quantity; - la même
variant; - un montant différent.
Trois des quatre champs à partir desquels on construit habituellement une estimation restent exactement où ils étaient. Une estimation calculée par prix × quantité sous-évalue, et rien dans la réponse ne le signale.
La signature lisible par une machine
Un champ marqué x-ui.affects_price: true qui n'est ni le quantityField ni listé dans x-pricing.variantFields déplace le montant sans sélectionner de palier. Pour un tel point d'accès, lisez le pricing du catalogue comme un plancher et prenez le montant dans un devis.
C'est la règle énoncée dans Catalogue de modèles et schémas, et la retouche d'image est l'endroit où vous la rencontrerez le plus souvent : le tableau d'images d'entrée porte fréquemment cette marque.
L'ampleur de l'ajout dépend du point d'accès et ne suit aucune proportion fixe. Le catalogue contient aujourd'hui des points d'accès où remplir le tableau coûte plusieurs fois le prix d'une référence unique, et d'autres où les références supplémentaires ne coûtent rien du tout — sans aucun moyen de les distinguer sinon en demandant. N'estimez donc pas le prix du tableau par analogie avec un point d'accès voisin, et ne déduisez pas le prix d'un tableau plein d'un devis à une seule référence.
Demandez le devis du corps que vous allez réellement envoyer
curl -sS https://api.spicyapi.ai/api/v1/jobs/quote \
-H "Authorization: Bearer $SPICY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"MODEL_ID_FROM_CATALOG","input":{"prompt":"your instruction","image_urls":["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]}}'Un devis exécute la même validation que la création de la tâche, ne coûte rien, ne lance rien et ne met rien de côté. Il renvoie estimatedCost, maxCharge, quantity et unit pour ce corps exact. Changez le tableau et redemandez un devis. Les détails, et la marche à suivre quand la réponse est perdue, sont dans Devis et compatibilité ; la place d'un devis dans une intégration réelle est décrite dans Mise en production.
Poids d'adaptation
Certains points d'accès permettent d'appliquer vos propres poids affinés par-dessus le modèle de base. Le champ canonique est loras, ou high_noise_loras et low_noise_loras lorsqu'un point d'accès expose ses étapes séparément.
Un tableau d'objets, avec ses propres règles à l'intérieur
Ces champs portent x-ui.widget: "object-list" : un tableau dont items est un schéma d'objet complet à part entière. Il a son propre required, son propre additionalProperties: false, et son propre type, intervalle et default pour chaque sous-champ. Une entrée type nomme un jeu de poids et la force avec laquelle l'appliquer.
{
"model": "MODEL_ID_FROM_CATALOG",
"input": {
"prompt": "your scene",
"loras": [
{ "path": "https://weights.example.com/style.safetensors", "scale": 1 },
{ "path": "https://weights.example.com/character.safetensors", "scale": 0.6 }
]
}
}La forme d'une entrée n'est pas fixe d'un point d'accès à l'autre. Ce qui compte comme fichier de poids — une adresse directe, ou une clé d'une bibliothèque intégrée — est restreint par le sous-schéma items propre au point d'accès, de même que les noms des sous-champs, le nombre d'entrées admises et lesquelles sont obligatoires. Lisez items, pas cet exemple. Le contrat Spicy Schema décrit le widget ; Catalogue de modèles et schémas montre comment en tirer un tableau imbriqué.
Une règle interne à ces entrées se lit mal : les valeurs par défaut pénètrent à l'intérieur des entrées du tableau. Un sous-champ à la fois listé dans items.required et doté d'un default est en pratique facultatif — omettez-le et la valeur par défaut est posée avant la validation. Un sous-champ sans valeur par défaut est refusé s'il manque. items.required ne signifie donc pas « doit figurer dans le JSON que j'envoie ».
D'où viennent les poids
path accepte l'une de ces trois choses :
- Un lien public vers le fichier
.safetensorsou, si le point d'accès l'admet, un identifiant de dépôt Hugging Face du typeowner/repo. Le modèle le télécharge au moment de la génération : le premier appel avec de nouveaux poids est donc sensiblement plus lent que les suivants. - Un envoi. Envoyez le fichier en
application/octet-streamavec le flux d'envoi direct, jusqu'à 2 GiB, puis utilisez l'URIspicy://f/…renvoyée. La validation (commit) vérifie qu'il s'agit d'un fichier safetensors complet. - Un import. Pour un fichier qui exige une connexion, comme un fichier d'un dépôt Hugging Face privé ou à accès restreint, ou un fichier Civitai protégé par une clé d'API, envoyez le lien et un jeton d'accès à
POST /api/v1/files/import. SpicyAPI télécharge le fichier une seule fois et renvoie une URIspicy://f/…. Le jeton ne sert qu'à ce téléchargement et n'est jamais conservé.
curl -sS https://api.spicyapi.ai/api/v1/files/import \
-H "Authorization: Bearer $SPICY_API_KEY" \
-H "Content-Type: application/json" \
--data "$(jq -n --arg url "https://huggingface.co/OWNER/REPO/blob/main/your_lora.safetensors" --arg token "$HF_TOKEN" '{url: $url, token: $token}')"url peut aussi être l'identifiant du dépôt (OWNER/REPO) ou la page du dépôt, lorsque celui-ci contient exactement un fichier .safetensors. La réponse a la même forme que celle d'une validation (commit) ; placez data.uri dans loras[].path. Les poids envoyés ou importés sont conservés un jour, comme tout autre envoi, et ne sont jamais partagés avec d'autres comptes. Passé ce délai, envoyez-les ou importez-les de nouveau.
La soumission contrôle le lien, pas les poids qu'il contient
Avant la création d'une tâche, chaque lien de poids est sondé. Un lien qui exige une connexion, renvoie un 404 ou ouvre une page web est refusé par un 400, sans aucune facturation, et le message désigne l'entrée concernée. Tout le reste passe : un hôte qui tombe plus tard, ou des poids qui ne se chargent pas, vous parviennent donc sous forme d'échec de tâche.
Les problèmes que le sondage ne peut pas voir reviennent plus tard, en échec de tâche, avec un errorCode parmi les neuf classes publiques de la plateforme et un message qui explique ce qui s'est passé ; le montant retenu pour la tâche est restitué. Voir Erreurs.
Le pire cas est plus silencieux encore : des poids construits pour un autre modèle de base peuvent se charger sans erreur et ne quasiment rien changer au résultat. Il n'y a pas d'erreur à lire, seulement une image qui ressemble à celle du modèle de base.
Vérifiez donc au déploiement
Sortez la vérification du chemin de requête et placez-la dans ce qui s'exécute quand vous livrez :
- Joignabilité. Une requête
HEADsur chaque adresse de poids que vous vous apprêtez à mettre en production. La plateforme sonde chaque lien à la création d'une tâche, mais un lien qui fonctionne aujourd'hui peut cesser de fonctionner la semaine prochaine, et une vérification au déploiement le détecte avant que votre trafic ne le fasse. - Effet. Une génération par nouveau fichier de poids, comparée au même prompt et à la même graine sans lui. Si les deux sont indiscernables, le fichier ne fait pas ce que vous croyez — vérifiez-le avant qu'un lot n'en dépende.
- Stabilité. Vos adresses doivent continuer de fonctionner tant qu'elles sont en production. Un fichier de poids qui disparaît se transforme en échecs de tâche sans cause apparente, plusieurs jours après le déploiement auquel on les attribuera.
Les conseils propres à un point d'accès sur ces champs — ce à quoi une valeur est réellement mesurée, quel réglage ralentit sensiblement un appel, quelles entrées le schéma accepte mais que vous regretterez — vivent dans paramNotes, sur le catalogue public, indexés par nom de paramètre et disponibles dans les dix langues de la documentation. C'est de la prose, jamais une règle ; c'est toujours inputSchema qui décide de ce qui est accepté. Voir Catalogue de modèles et schémas.
Asynchrone, comme tout autre modèle média
Les points d'accès image sont des tâches, pas des appels requête-réponse. Vous créez une tâche, puis vous récupérez le résultat par interrogation ou par webhook ; queued et running ne sont pas terminaux, succeeded, failed et expired le sont. La référence sur les états est Tâches asynchrones.
Les images se terminent plus vite que les vidéos, ce qui pousse à écrire l'appel comme s'il était synchrone. Résistez : une interrogation bornée est assez brève ici pour en donner l'illusion, mais le code doit quand même gérer une tâche encore en cours quand le budget d'attente est épuisé, et confier cela à un webhook passe mieux à l'échelle que de maintenir une requête ouverte. Le polling utilise le quota de lecture du compte, indépendamment de l’envoi des tâches. Un polling excessif peut limiter les requêtes d’état ; voir Limites de débit.
Lire le résultat
Une tâche réussie liste ses fichiers dans output.assets[]. Trois choses à savoir sur cette liste pour les points d'accès image :
- Elle peut contenir plus d'un fichier. Quand
num_outputsdépasse un, vous recevez une ressource par image. Écrivez le gestionnaire comme une boucle dès le départ ; un gestionnaire qui litassets[0]jette en silence tout ce que vous avez payé. - C'est
mimequi décide de l'extension, ni le nom du point d'accès ni le prompt. Quand un point d'accès exposeoutput_format, ce champ choisit l'encodage ; sinon, l'encodage est propre au point d'accès etmimeest le seul moyen de savoir ce que vous avez reçu. - Un fichier encore en cours de copie porte
pending: trueet pas d'url. Les résultats sont copiés dans le stockage de SpicyAPI avant d'être remis, ce qui prend quelques secondes après que la tâche a signalé sa réussite. Relisez la tâche plutôt que de traiter l'absence de lien comme un échec.
Les liens de téléchargement durent environ vingt minutes et ne demandent pas de clé d'API ; relisez la tâche pour en obtenir un nouveau. Les fichiers générés sont conservés quatorze jours au plus, moins si la rétention de votre compte est plus courte. Voir Résultats et téléchargements et Rétention et destruction.
Lire un refus
Une requête refusée ne coûte rien et ne crée rien : le point d'accès de devis fait donc aussi office de validateur gratuit pour le corps que vous vous apprêtez à soumettre.
Tous les problèmes sont signalés, pas seulement le premier. Le message les joint par ; , si bien qu'un seul devis refusé peut vous dire tout ce qui cloche :
prompt: is required; loras.0.path: is required; loras.0.scale: must be at most 4; bogus: is not a supported fieldUn problème à l'intérieur d'un tableau désigne l'entrée. Le pointeur s'écrit champ.indice.sous-champ, avec un indice partant de zéro :
loras.1.scale: must be at most 4Transmettez ce pointeur à qui devra corriger. Avec plusieurs entrées en jeu, savoir laquelle est hors bornes fait la différence entre une correction d'une ligne et une bissection.
Les formes que vous rencontrerez :
| Message | Ce que cela veut dire | Que faire |
|---|---|---|
X: is required | X figure dans la liste required racine | L'ajouter. Sur les points d'accès de retouche, c'est en général prompt ou le champ d'image |
X: is not a supported field | X n'est pas dans properties ; les champs inconnus sont refusés, jamais ignorés | Le retirer, ou employer l'orthographe de ce point d'accès — voir Taille de sortie |
X: must be one of: … | La valeur est hors enum | Le message énumère les valeurs admises ; enum et default sont dans le schéma |
X: must contain at least N items | En dessous de minItems | Un tableau exigé par le point d'accès ne peut pas être envoyé vide |
X: must contain at most N items | Au-dessus de maxItems | Lire le plafond de ce point d'accès ; il n'est pas le même partout |
X: must be an array | La valeur n'a pas le type déclaré | Envelopper l'entrée unique dans une liste |
X: must be at most N characters | Au-dessus de maxLength | Les prompts ont aussi un plafond par point d'accès |
X.<n>.<sub>: is required | Un sous-champ de l'entrée <n> est obligatoire et sans valeur par défaut | L'ajouter à cette entrée seulement |
X.<n>.<sub>: is not a supported field | Le schéma d'entrée pose additionalProperties: false | Retirer cette clé de cette entrée |
X.<n>.<sub>: must be at most N | Le sous-champ est hors de son propre intervalle | Le borner ; les intervalles sont par sous-champ, pas par tableau |
Si le refus tombe alors que vous attendiez un succès, la cause habituelle est un schéma périmé. Relisez le catalogue après un 400, et ne conservez jamais d'instantané permanent — Mettre le catalogue en cache indique combien de temps une copie reste valable. Dépannage couvre le reste.
Étapes suivantes
Catalogue de modèles et schémas
Le catalogue en direct, les paliers tarifaires et votre propre tableau de paramètres à partir d'inputSchema
Contrat Spicy Schema
Noms de champs canoniques, annotations x-ui, champs conditionnels et format de résultat
Médias
Téléverser une image et en faire une référence spicy://
Devis et compatibilité
Chiffrer une requête précise avant de la payer
Échange de sujet
Les points d'accès de retouche qui prennent des images et aucun prompt
Tutoriel : transformer une photo en vidéo
Le même parcours pour la vidéo, avec la CLI, le SDK et du HTTP brut
Erreurs
Tous les codes d'erreur, et ceux pour lesquels réessayer sert à quelque chose
Mise en production
Ce qu'il faut ajouter avant que de vrais utilisateurs en dépendent
Tutoriel : transformer une image en vidéo
Partez d’une image enregistrée sur votre ordinateur et repartez avec un fichier vidéo téléchargé. Chaque étape montre la CLI, le SDK TypeScript et le HTTP simple.
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.

