Le moteur de home staging virtuel qui fait tourner l'application web est désormais disponible sous forme d'API HTTP. Vous envoyez la photo d'une pièce, vous indiquez un type de pièce et un style de mobilier, et l'image meublée revient dans la même réponse. Cela coûte $0.15 l'image, sans abonnement, sans minimum et sans facture mensuelle, et la documentation de référence se trouve sur stagify.ai/developers.html.

Cet article est ce qu'une page de référence ne peut pas être : pourquoi l'API a la forme qu'elle a, et la poignée de choses qui vous feront trébucher le premier après-midi.

Toute l'API tient en un seul endpoint

Il existe exactement un appel qui coûte de l'argent.

curl https://stagify.ai/api/v1/renders \
  -H "Authorization: Bearer $STAGIFY_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -F "image=@living-room.jpg" \
  -F "roomType=Living room" \
  -F "furnitureStyle=modern" \
  -F "labelVirtuallyStaged=true"

C'est toute l'intégration. La réponse contient l'image meublée sous forme de data URL, ainsi qu'un statut et un identifiant de requête. Tout le reste de l'API est gratuit et en lecture seule : GET /api/v1/options vous donne les valeurs acceptées de chaque enum, GET /api/v1/me vous dit si votre clé fonctionne, GET /api/v1/credits indique le solde et GET /api/v1/renders/{id} retrouve un rendu précis.

Les requêtes sont en multipart/form-data, parce que vous envoyez une photographie et que du base64 dans un corps JSON vous coûterait un tiers d'octets en plus pour rien. Les images sont en JPEG, PNG ou WebP jusqu'à 25 Mo, et c'est le type MIME qui est vérifié, pas l'extension du fichier.

Pourquoi il n'y a pas de file d'attente

Le design évident pour une API d'images lente consiste à accepter la requête, renvoyer un identifiant de tâche et laisser le client interroger le résultat. Nous ne l'avons délibérément pas fait, et la raison mérite d'être dite, car vu de l'extérieur cela ressemble à un raccourci, et ce n'en est pas un.

Cette application n'a aucune file d'attente. En ajouter une uniquement pour l'API reviendrait à introduire une file dont les tâches vivent dans un seul processus, sur un hôte qui redémarre à chaque déploiement. Quand cela arrive au milieu d'un rendu, le crédit du client est déjà dépensé et il ne reste aucune socket pour le lui dire. Cette panne-là est strictement pire qu'une connexion coupée, car une connexion coupée peut être retentée avec la même clé d'idempotence et ne coûte rien.

Un rendu garde donc la connexion ouverte jusqu'à la fin, ce qui prend de quelques secondes à deux ou trois minutes. Réglez le read timeout de votre client sur 300 secondes. La valeur par défaut de la plupart des bibliothèques HTTP est de 30 ou 60, et un client qui abandonne au bout de 30 secondes donnera l'impression d'une API qui échoue sans arrêt tout en dépensant discrètement des crédits pour des rendus qu'il ne récupère jamais.

Conçue pour évoluer plus tard sans rien casser chez vous : le corps de réponse en cas de succès contient déjà status: "succeeded", et GET /api/v1/renders/{id} existe déjà. Si nous ajoutons un jour une file d'attente, elle arrivera sous la forme d'une nouvelle valeur de status sur le même endpoint, et non d'une nouvelle API. Traitez tout statut autre que succeeded comme « va interroger le GET » et votre client est déjà prêt pour la suite.

Le même raisonnement plafonne variations à 1. Une requête, c'est un crédit, c'est une image, c'est un remboursement, sans aucune arithmétique de répartition partielle du côté de l'argent. Si vous voulez trois versions d'une pièce, lancez trois requêtes en parallèle : vous obtenez ainsi un meilleur parallélisme que ce que le fan-out vous a jamais donné.

Les crédits prépayés, et pourquoi c'est une mesure de sécurité

Vous achetez des crédits à l'avance. Chaque rendu réussi en dépense un, et un rendu qui échoue est remboursé automatiquement : vous ne payez donc jamais une photo que vous n'avez pas reçue. Les packs démarrent à 20 crédits pour $3, et le prix à l'image baisse à mesure qu'ils grossissent, jusqu'à $0.12 l'image sur le pack de 500 crédits. Le tableau à jour est sur la page de documentation de l'API, généré à partir des mêmes données que celles sur lesquelles le webhook de paiement facture : il ne peut donc pas diverger de ce qui vous est réellement facturé.

La facturation à l'usage aurait été plus simple à construire. Le prépayé est meilleur pour vous sur un point précis : une clé volée ne peut vous coûter que le solde qui reste dessus. Pas de facture en fin de mois, pas de dépassement, et aucun scénario où une clé fuitée devient une note à quatre chiffres avant que quiconque s'en aperçoive. L'arrêt net à zéro est le but et non une limitation, et c'est aussi pourquoi chaque réponse porte un en-tête X-Stagify-Credits-Remaining : vous voyez le mur approcher sans avoir à l'interroger.

Les clés sont destinées à un usage de serveur à serveur uniquement. L'API n'envoie aucun en-tête CORS, le JavaScript d'un navigateur ne peut donc pas l'appeler, et c'est délibéré, pas un oubli que nous comptons corriger. Une clé accessible depuis un navigateur est une clé fuitée. Nous affichons une nouvelle clé exactement une fois, n'en stockons qu'un hachage, et la révoquons sur-le-champ depuis le tableau de bord des clés d'API.

Quatre choses qui vous feront trébucher

Toute API a des comportements parfaitement raisonnables une fois expliqués et déroutants avant de l'être. Voici les nôtres, dits clairement plutôt que laissés dans un tableau de paramètres.

  1. roomType est sensible à la casse et aux espaces, et une valeur erronée n'est pas rejetée. Elle est écrite telle quelle dans le prompt en texte libre, donc Living Room n'est pas Living room. Vous obtiendrez un rendu, mais pas celui que vous vouliez, et vous l'aurez payé. L'ensemble accepté est Bedroom, Living room, Dining room, Kitchen, Office, Bathroom, Outdoors et Dorm. Récupérez-le depuis GET /api/v1/options plutôt que de le retaper dans votre propre menu déroulant.
  2. Un furnitureStyle non reconnu devient silencieusement standard. Même classe de problème, symptôme plus discret : une faute de frappe dans un nom de style ne déclenche pas d'erreur, elle vous donne simplement le rendu par défaut sur toutes les images du lot.
  3. removeFurniture accepte true ou on, et rien d'autre. Ni 1, ni yes. Ces deux-là se lisent comme false, et vous récupérez une photo meublée avec l'ancien canapé toujours dedans. Son compagnon keepFurniture prend du texte libre nommant ce qu'il faut laisser en place, jusqu'à 500 caractères, et n'est lu que lorsque removeFurniture est effectivement activé.
  4. Envoyez une Idempotency-Key à chaque requête. L'omettre est autorisé et signifie aucune protection contre les rejeux, ce qui est le comportement par défaut honnête ; nous ne la déduirons pas de votre photo, car deux rendus réellement distincts de la même pièce entreraient alors en collision et le second appelant recevrait l'image du premier. Réessayez avec la même valeur et une requête terminée est rejouée depuis le stockage avec X-Stagify-Replayed: true et sans second débit. Réutilisez-en une avec des paramètres différents et vous obtenez 422 IDEMPOTENCY_KEY_REUSED, c'est-à-dire l'API qui vous dit que votre générateur de clés a un bug.

Au-delà de cela, les paramètres à connaître reflètent ceux de l'application web : additionalPrompt pour des consignes en texte libre, et jusqu'à cinq photos de référence furnitureImage auxquelles le modèle peut se conformer.

L'étiquette de divulgation ne peut pas être désactivée

La plupart des règles des MLS, et les recommandations de la NAR qui les sous-tendent, exigent qu'une photo d'annonce meublée numériquement dise qu'elle l'est ; depuis janvier 2026, l'AB 723 de Californie l'impose par la loi. Nous en avons écrit la version État par État en détail.

labelVirtuallyStaged grave la mention dans les pixels plutôt que de l'attacher en métadonnées, parce que les métadonnées sont supprimées par à peu près tous les outils par lesquels une image passe en route vers un portail. Vous en contrôlez l'apparence : stampStyle choisit entre dark, light, minimal et banner, stampScale la dimensionne entre 0.7 et 1.6, et stampLang l'affiche dans l'une des onze langues.

Ce que vous ne contrôlez pas, c'est le mode de défaillance. Si l'étiquette est demandée et ne peut pas être appliquée, l'image est retenue plutôt que livrée sans étiquette. L'appel répond 500 DISCLOSURE_STAMP_FAILED et votre crédit est remboursé. Aucun indicateur ne permet de désactiver cela, et aucun réglage par clé ne l'éteint discrètement. L'alternative consisterait à livrer une photo meublée non étiquetée à un client qui a explicitement demandé qu'elle le soit : c'est le seul bug de ce système capable de mettre quelqu'un face à un régulateur.

Les erreurs, et la seule règle à leur sujet

Chaque échec répond avec un corps JSON portant à la fois une chaîne error et un code. Branchez sur le code, jamais sur le message. Les messages sont réécrits ; les codes, non.

Celles que vous rencontrerez vraiment en production sont 402 INSUFFICIENT_CREDITS (rechargez ; le corps contient le solde restant), 409 CONCURRENCY_LIMIT (trop de rendus en cours sur une même clé ; la marge par défaut est de trois à la fois, alors patientez une seconde et réessayez), 409 REQUEST_IN_FLIGHT (vous avez retenté une clé d'idempotence encore en cours d'exécution, donc attendez au lieu d'insister), 429 RATE_LIMITED, et 422 NO_IMAGE_GENERATED, qui signifie que le modèle n'a rien renvoyé et qui est remboursé automatiquement. La limite de débit des rendus est actuellement de 60 par clé par tranche de cinq minutes ; s'il vous en faut davantage, ou une facture plutôt qu'une carte, écrivez-nous.

Remarquez le motif commun à tout cela : tout ce qui échoue après un débit est remboursé. Cet invariant est la raison pour laquelle l'API est synchrone et celle pour laquelle variations est plafonné à un, et c'est ce que nous garderions quitte à abandonner des fonctionnalités.

À qui cela s'adresse, et à qui non

L'API existe pour les cas où le staging est une étape dans le pipeline de quelqu'un d'autre plutôt qu'une chose qu'une personne s'assoit pour faire :

Ce n'est absolument pas fait pour une personne qui meuble une seule maison. Si c'est votre cas, l'application web fait plus que l'API, puisque Masking Studio et l'AI Designer n'ont pas d'équivalent dans l'API, et elle est gratuite, sans filigrane. Payer $0.15 pour faire en code ce que vous pouvez faire pour rien dans un navigateur serait un choix étrange, et nous préférons vous le dire plutôt que de vous vendre un pack de crédits.

Pour commencer

Créez une clé sur la page des clés d'API, achetez le plus petit pack et dépensez un crédit sur une photo dont vous connaissez déjà le résultat attendu. Lisez ensuite la référence pour de bon avant d'écrire votre boucle de traitement par lots, en particulier le tableau d'idempotence. Vingt crédits, c'est $3, et c'est largement assez pour savoir si cela convient à votre pipeline.

Sources et notes