spicyapiDocumentation
Guides
Contenu principal

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 cherchezLisez plutôt
Votre première image, de la clé au fichier, d'un bout à l'autreDémarrage rapide
Le même parcours pour la vidéo, étape par étapeTutoriel : transformer une photo en vidéo
Tous les champs d'un modèle précisSon 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.

Refusé par un point d'accès de retouche
{
  "model": "MODEL_ID_FROM_CATALOG",
  "input": {
    "image_urls": ["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]
  }
}
prompt: is required

L'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 :

ChampFormeCe que cela signifie d'ordinaire
image_urlune référence d'imageIl y a exactement une image de sujet, et le point d'accès n'a pas de notion de seconde
image_urlstableau de références d'imageLes images sont la matière retouchée ou combinée, dans l'ordre auquel la consigne se réfère
reference_image_urlstableau de références d'imageLes 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 items

Lisez 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 :

OrthographeChampsRemarques
Séparéeaspect_ratio et resolutionLa forme du cadre et le palier de pixels se choisissent séparément
PréréglagesizeUne valeur d'énumération fixe les deux d'un coup
Explicitewidth et heightPixels 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 field

Rien 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 :

  • resolution sélectionne normalement un palier tarifaire. Il apparaît dans le devis sous variant, 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

Chiffrer cette requête précise avant de payer
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.

Deux entrées appliquées ensemble
{
  "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 .safetensors ou, si le point d'accès l'admet, un identifiant de dépôt Hugging Face du type owner/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-stream avec le flux d'envoi direct, jusqu'à 2 GiB, puis utilisez l'URI spicy://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 URI spicy://f/…. Le jeton ne sert qu'à ce téléchargement et n'est jamais conservé.
Importer un LoRA depuis un dépôt Hugging Face privé
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 :

  1. Joignabilité. Une requête HEAD sur 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.
  2. 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.
  3. 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_outputs dépasse un, vous recevez une ressource par image. Écrivez le gestionnaire comme une boucle dès le départ ; un gestionnaire qui lit assets[0] jette en silence tout ce que vous avez payé.
  • C'est mime qui décide de l'extension, ni le nom du point d'accès ni le prompt. Quand un point d'accès expose output_format, ce champ choisit l'encodage ; sinon, l'encodage est propre au point d'accès et mime est le seul moyen de savoir ce que vous avez reçu.
  • Un fichier encore en cours de copie porte pending: true et 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 field

Un 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 4

Transmettez 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 :

MessageCe que cela veut direQue faire
X: is requiredX figure dans la liste required racineL'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 fieldX n'est pas dans properties ; les champs inconnus sont refusés, jamais ignorésLe retirer, ou employer l'orthographe de ce point d'accès — voir Taille de sortie
X: must be one of: …La valeur est hors enumLe message énumère les valeurs admises ; enum et default sont dans le schéma
X: must contain at least N itemsEn dessous de minItemsUn tableau exigé par le point d'accès ne peut pas être envoyé vide
X: must contain at most N itemsAu-dessus de maxItemsLire le plafond de ce point d'accès ; il n'est pas le même partout
X: must be an arrayLa valeur n'a pas le type déclaréEnvelopper l'entrée unique dans une liste
X: must be at most N charactersAu-dessus de maxLengthLes prompts ont aussi un plafond par point d'accès
X.<n>.<sub>: is requiredUn sous-champ de l'entrée <n> est obligatoire et sans valeur par défautL'ajouter à cette entrée seulement
X.<n>.<sub>: is not a supported fieldLe schéma d'entrée pose additionalProperties: falseRetirer cette clé de cette entrée
X.<n>.<sub>: must be at most NLe sous-champ est hors de son propre intervalleLe 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

Sur cette page