Il motore di home staging virtuale che sta dietro all'app web è ora disponibile come API HTTP. Invii la foto di una stanza, indichi un tipo di ambiente e uno stile di arredamento, e l'immagine arredata torna nella stessa risposta. Costa $0.15 a immagine, non c'è abbonamento, né minimo, né fattura mensile, e la documentazione di riferimento è su stagify.ai/developers.html.

Questo articolo è ciò che una pagina di riferimento non può essere: perché l'API ha la forma che ha, e la manciata di cose su cui inciamperai il primo pomeriggio.

Tutta l'API è un solo endpoint

C'è esattamente una chiamata che costa denaro.

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"

Questa è l'integrazione. La risposta porta l'immagine arredata come data URL, insieme a uno stato e a un id di richiesta. Tutto il resto dell'API è gratuito e in sola lettura: GET /api/v1/options ti dà i valori ammessi per ogni enum, GET /api/v1/me ti dice se la tua chiave funziona, GET /api/v1/credits riporta il saldo e GET /api/v1/renders/{id} recupera un singolo render.

Le richieste sono multipart/form-data, perché stai caricando una fotografia e il base64 in un corpo JSON ti costerebbe un terzo di byte in più per nulla. Le immagini sono JPEG, PNG o WebP fino a 25 MB, e ciò che viene controllato è il tipo MIME, non l'estensione del file.

Perché non c'è una coda di lavori

Il design ovvio per un'API di immagini lenta è accettare la richiesta, restituire un id di job e far interrogare il risultato al client. Deliberatamente non l'abbiamo fatto, e la ragione merita di essere detta, perché da fuori sembra una scorciatoia e non lo è.

Questa applicazione non ha alcuna coda di lavori. Aggiungerne una solo per l'API significherebbe introdurre una coda i cui job vivono in un unico processo, su un host che si riavvia a ogni deploy. Quando succede a metà di un render, il credito del cliente è già speso e non resta alcun socket per dirgli qualcosa. Quel fallimento è strettamente peggiore di una connessione caduta, perché una connessione caduta si può ritentare con la stessa chiave di idempotenza e non costa nulla.

Quindi un render tiene aperta la connessione finché non finisce, e ci vuole da qualche secondo a un paio di minuti. Imposta il read timeout del tuo client a 300 secondi. Il valore predefinito nella maggior parte delle librerie HTTP è 30 o 60, e un client che si arrende a 30 secondi sembrerà un'API che fallisce di continuo, mentre in silenzio spende crediti per render che non raccoglie mai.

Costruita per spostarsi in futuro senza romperti nulla: il corpo di successo porta già status: "succeeded", e GET /api/v1/renders/{id} esiste già. Se un giorno aggiungeremo una coda, arriverà come un nuovo valore di status sullo stesso endpoint, non come una nuova API. Tratta qualsiasi stato diverso da succeeded come «vai a interrogare la GET» e il tuo client è già pronto per il futuro.

Lo stesso ragionamento limita variations a 1. Una richiesta è un credito è un'immagine è un rimborso, senza aritmetica di distribuzione parziale in nessun punto dei soldi. Se vuoi tre versioni di una stanza, lancia tre richieste in parallelo: ottieni così un parallelismo migliore di quello che il fan-out ti ha mai dato.

Crediti prepagati, e perché sono una misura di sicurezza

Compri i crediti in anticipo. Ogni render riuscito ne spende uno, e un render fallito viene rimborsato automaticamente, quindi non paghi mai una foto che non hai ricevuto. I pacchetti partono da 20 crediti per $3, e il prezzo per immagine scende man mano che crescono, fino a $0.12 a immagine sul pacchetto da 500 crediti. La tabella aggiornata è sulla pagina di documentazione dell'API, generata dagli stessi dati su cui addebita il webhook di pagamento, quindi non può divergere da ciò che ti viene effettivamente fatturato.

La fatturazione a consumo sarebbe stata più facile da costruire. Il prepagato è meglio per te su un punto specifico: una chiave rubata può costarti al massimo il saldo che le resta. Non c'è fattura a fine mese, non ci sono eccedenze, e non esiste lo scenario in cui una chiave trapelata diventa un conto a quattro cifre prima che qualcuno se ne accorga. Lo stop netto a zero è l'obiettivo e non un limite, ed è anche il motivo per cui ogni risposta porta un header X-Stagify-Credits-Remaining: puoi vedere il muro avvicinarsi senza interrogarlo.

Le chiavi sono pensate solo per l'uso da server a server. L'API non invia header CORS, quindi il JavaScript del browser non può chiamarla, e questo è deliberato, non un'omissione che intendiamo correggere. Una chiave raggiungibile da un browser è una chiave trapelata. Mostriamo una chiave nuova esattamente una volta, ne conserviamo solo un hash e la revochiamo all'istante dalla dashboard delle chiavi API.

Quattro cose su cui inciamperai

Ogni API ha comportamenti perfettamente ragionevoli una volta spiegati e sconcertanti prima. Ecco i nostri, detti chiaramente invece che lasciati in una tabella di parametri.

  1. roomType distingue maiuscole e spazi, e un valore sbagliato non viene rifiutato. Viene scritto nel prompt come testo libero, quindi Living Room non è Living room. Otterrai un render, solo non quello che volevi, e lo avrai pagato. L'insieme ammesso è Bedroom, Living room, Dining room, Kitchen, Office, Bathroom, Outdoors e Dorm. Prendilo da GET /api/v1/options invece di ridigitarlo nel tuo menu a tendina.
  2. Un furnitureStyle non riconosciuto diventa silenziosamente standard. Stessa classe di problema, sintomo più silenzioso: un refuso nel nome di uno stile non dà errore, ti restituisce semplicemente l'aspetto predefinito su ogni immagine del lotto.
  3. removeFurniture accetta true oppure on, e nient'altro. Non 1, non yes. Entrambi vengono letti come false, e ottieni una foto arredata con il vecchio divano ancora dentro. Il suo compagno keepFurniture accetta testo libero che indica cosa lasciare al suo posto, fino a 500 caratteri, e viene letto solo quando removeFurniture è effettivamente impostato.
  4. Invia una Idempotency-Key a ogni richiesta. Ometterla è consentito e significa nessuna protezione dai replay, che è il comportamento predefinito onesto; non la deriveremo dalla tua foto, perché allora due render genuinamente distinti della stessa stanza si scontrerebbero e al secondo chiamante verrebbe consegnata l'immagine del primo. Riprova con lo stesso valore e una richiesta completata viene riprodotta dallo storage con X-Stagify-Replayed: true e senza un secondo addebito. Riusane una con parametri diversi e ottieni 422 IDEMPOTENCY_KEY_REUSED, cioè l'API che ti dice che il tuo generatore di chiavi ha un bug.

Oltre a questi, i parametri che vale la pena conoscere rispecchiano l'app web: additionalPrompt per indicazioni in testo libero, e fino a cinque foto di riferimento furnitureImage a cui il modello può ispirarsi.

L'etichetta di divulgazione non si può disattivare

La maggior parte delle regole delle MLS, e le linee guida NAR che le sostengono, richiedono che una foto di annuncio arredata digitalmente dichiari di esserlo; da gennaio 2026 l'AB 723 della California lo impone per legge. Ne abbiamo scritto la versione stato per stato in modo esteso.

labelVirtuallyStaged incide la dicitura nei pixel invece di allegarla come metadato, perché i metadati vengono rimossi da praticamente ogni strumento a valle attraverso cui passa un'immagine diretta a un portale. Il suo aspetto lo controlli tu: stampStyle sceglie tra dark, light, minimal e banner, stampScale la dimensiona tra 0.7 e 1.6, e stampLang la rende in una qualsiasi di undici lingue.

Ciò che non controlli è la modalità di fallimento. Se l'etichetta è richiesta e non può essere applicata, l'immagine viene trattenuta invece che consegnata senza etichetta. La chiamata risponde 500 DISCLOSURE_STAMP_FAILED e il tuo credito viene rimborsato. Non c'è alcun flag per disattivarlo, né un valore predefinito per chiave che lo spenga in sordina. L'alternativa è consegnare una foto arredata senza etichetta a un cliente che ha chiesto esplicitamente di etichettarla: è l'unico bug di questo sistema capace di mettere qualcuno davanti a un regolatore.

Gli errori, e l'unica regola che li riguarda

Ogni fallimento risponde con un corpo JSON che porta sia una stringa error sia un code. Ramifica sul codice, mai sul messaggio. I messaggi vengono riscritti; i codici no.

Quelli che incontrerai davvero in produzione sono 402 INSUFFICIENT_CREDITS (ricarica; il corpo porta il saldo residuo), 409 CONCURRENCY_LIMIT (troppi render in corso su una stessa chiave; il margine predefinito è di tre alla volta, quindi aspetta un secondo e riprova), 409 REQUEST_IN_FLIGHT (hai ritentato una chiave di idempotenza ancora in esecuzione, quindi aspetta invece di insistere), 429 RATE_LIMITED e 422 NO_IMAGE_GENERATED, che significa che il modello non ha restituito nulla e viene rimborsato automaticamente. Il limite sui render è attualmente di 60 per chiave ogni cinque minuti; se ti serve di più, o una fattura invece di una carta, scrivici.

Nota il motivo ricorrente in tutto questo: tutto ciò che fallisce dopo un addebito viene rimborsato. Quell'invariante è la ragione per cui l'API è sincrona e la ragione per cui variations è limitato a uno, ed è la cosa per cui rinunceremmo a delle funzionalità.

A chi serve, e a chi no

L'API esiste per i casi in cui lo staging è un passaggio nella pipeline di qualcun altro e non qualcosa che una persona si siede a fare:

Non è affatto pensata per chi arreda una sola casa. Se sei tu, l'app web fa più dell'API, dato che Masking Studio e l'AI Designer non hanno un equivalente nell'API, ed è gratuita, senza filigrana. Pagare $0.15 per fare in codice ciò che puoi fare gratis in un browser sarebbe una scelta strana, e preferiamo dirtelo piuttosto che venderti un pacchetto di crediti.

Per iniziare

Crea una chiave nella pagina delle chiavi API, compra il pacchetto più piccolo e spendi un credito su una foto di cui conosci già il risultato. Poi leggi per bene la documentazione prima di scrivere il ciclo in blocco, in particolare la tabella dell'idempotenza. Venti crediti sono $3, e bastano ampiamente per scoprire se questo si incastra nella tua pipeline.

Fonti e note