De engine voor virtuele woningstyling achter de webapp is nu beschikbaar als HTTP-API. Je post een foto van een kamer, noemt een kamertype en een meubelstijl, en het gestylede beeld komt in dezelfde response terug. Het kost $0.15 per beeld, er is geen abonnement, geen minimum en geen maandfactuur, en de referentiedocumentatie staat op stagify.ai/developers.html.
Dit artikel is wat een referentiepagina niet kan zijn: waarom de API de vorm heeft die hij heeft, en de handvol dingen waarover je de eerste middag struikelt.
De hele API is één endpoint
Er is precies één call die geld kost.
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"
Dat is de integratie. De response bevat het gestylede beeld als data-URL, samen met een status en een request-id. Al het andere in de API is gratis en alleen-lezen: GET /api/v1/options geeft je de toegestane waarden voor elke enum, GET /api/v1/me vertelt of je key werkt, GET /api/v1/credits meldt het saldo, en GET /api/v1/renders/{id} zoekt één render terug.
Requests gaan als multipart/form-data, omdat je een foto uploadt en base64 in een JSON-body je voor niets een derde meer bytes zou kosten. Beelden zijn JPEG, PNG of WebP tot 25 MB, en er wordt gecontroleerd op het MIME-type, niet op de bestandsextensie.
Waarom er geen job queue is
Het voor de hand liggende ontwerp voor een trage beeld-API is: het verzoek aannemen, een job-id teruggeven en de client laten pollen op het resultaat. Dat hebben we bewust niet gedaan, en de reden is het zeggen waard, want van buitenaf lijkt het een kortere weg en dat is het niet.
Deze app heeft helemaal geen job queue. Er een toevoegen puur voor de API zou betekenen dat je een queue introduceert waarvan de jobs in één proces leven, op een host die bij elke deploy herstart. Gebeurt dat midden in een render, dan is het credit van de klant al uitgegeven en is er geen socket meer over om hem iets te vertellen. Dat falen is strikt genomen erger dan een verbroken verbinding, want een verbroken verbinding kun je opnieuw proberen met dezelfde idempotency key en kost niets.
Een render houdt de verbinding dus vast tot hij klaar is, en dat duurt van een paar seconden tot een paar minuten. Zet de read timeout van je client op 300 seconden. De standaard in de meeste HTTP-bibliotheken is 30 of 60, en een client die er na 30 seconden mee stopt lijkt op een API die constant faalt, terwijl hij stilletjes credits uitgeeft aan renders die hij nooit ophaalt.
status: "succeeded", en GET /api/v1/renders/{id} bestaat al. Als we ooit een queue toevoegen, komt die als een nieuwe status-waarde op hetzelfde endpoint, niet als een nieuwe API. Behandel elke status behalve succeeded als “ga de GET pollen” en je client is nu al toekomstbestendig.
Dezelfde redenering maximeert variations op 1. Eén request is één credit is één beeld is één terugbetaling, zonder deelrekensommen bij het geld. Wil je drie versies van een kamer, stuur dan drie gelijktijdige requests: zo krijg je meer parallellisme dan de fan-out je ooit gaf.
Prepaid credits, en waarom dat een beveiligingsmaatregel is
Je koopt credits vooraf. Elke geslaagde render kost er één, en een render die mislukt wordt automatisch terugbetaald, dus je betaalt nooit voor een foto die je niet hebt gekregen. Pakketten beginnen bij 20 credits voor $3, en de prijs per beeld daalt naarmate ze groter worden, tot $0.12 per beeld bij het pakket van 500 credits. De actuele tabel staat op de API-documentatiepagina, gegenereerd uit dezelfde data waartegen de betaal-webhook afrekent, zodat hij niet uiteen kan lopen met wat je daadwerkelijk betaalt.
Afrekenen op verbruik was makkelijker te bouwen geweest. Prepaid is op één specifiek punt beter voor jou: een gestolen key kan je hooguit het saldo kosten dat er nog op staat. Geen factuur aan het eind van de maand, geen overschrijding, en geen scenario waarin een gelekte key een rekening van vier cijfers wordt voordat iemand het merkt. De harde stop op nul is het doel en geen beperking, en daarom draagt ook elke response een X-Stagify-Credits-Remaining-header: je ziet de muur naderen zonder ernaar te hoeven pollen.
Keys zijn uitsluitend bedoeld voor gebruik van server naar server. De API stuurt geen CORS-headers, dus JavaScript in de browser kan hem niet aanroepen, en dat is opzet en geen omissie die we van plan zijn te repareren. Een key die vanuit een browser bereikbaar is, is een gelekte key. We tonen een nieuwe key precies één keer, bewaren er alleen een hash van, en trekken hem ter plekke in vanaf het dashboard voor API-keys.
Vier dingen waarover je struikelt
Elke API heeft gedrag dat volkomen redelijk is zodra het is uitgelegd en verbijsterend daarvoor. Hier is het onze, gewoon gezegd in plaats van achtergelaten in een parametertabel.
roomTypeis hoofdletter- en spatiegevoelig, en een verkeerde waarde wordt niet geweigerd. Hij wordt als vrije tekst in de prompt geschreven, dusLiving Roomis nietLiving room. Je krijgt een render, alleen niet die je bedoelde, en je hebt ervoor betaald. De toegestane set isBedroom,Living room,Dining room,Kitchen,Office,Bathroom,OutdoorsenDorm. Haal hem op uitGET /api/v1/optionsin plaats van hem in je eigen dropdown over te typen.- Een niet-herkende
furnitureStylewordt stilzwijgendstandard. Dezelfde soort fout, stiller symptoom: een typefout in een stijlnaam geeft geen error, hij geeft je gewoon de standaardlook op elk beeld in de batch. removeFurnitureaccepteerttrueofon, en niets anders. Niet1, nietyes. Die worden allebei als false gelezen, en je krijgt een gestylede foto met de oude bank er nog in. Zijn tegenhangerkeepFurnitureneemt vrije tekst die benoemt wat moet blijven staan, tot 500 tekens, en wordt alleen gelezen alsremoveFurnituredaadwerkelijk is gezet.- Stuur bij elk verzoek een
Idempotency-Keymee. Hem weglaten mag en betekent geen replaybescherming, wat de eerlijke standaard is; we leiden hem niet af uit je foto, want dan zouden twee werkelijk losse renders van dezelfde kamer botsen en zou de tweede aanroeper het beeld van de eerste krijgen. Probeer opnieuw met dezelfde waarde en een afgeronde aanvraag wordt uit de opslag herhaald metX-Stagify-Replayed: trueen zonder tweede afschrijving. Hergebruik er een met andere parameters en je krijgt422 IDEMPOTENCY_KEY_REUSED, oftewel de API die je vertelt dat je key-generator een bug heeft.
Daarnaast spiegelen de parameters die het kennen waard zijn de webapp: additionalPrompt voor sturing in vrije tekst, en tot vijf furnitureImage-referentiefoto's waar het model zich op kan richten.
Het vermeldingslabel kan niet worden uitgezet
De meeste MLS-regels, en de NAR-richtlijn erachter, eisen dat een gestylede advertentiefoto vermeldt dat hij gestyled is; sinds januari 2026 eist Californië's AB 723 dat bij wet. We hebben daar de versie per staat van geschreven, uitgebreid.
labelVirtuallyStaged brandt de vermelding in de pixels in plaats van hem als metadata mee te geven, omdat metadata door zowat elke tool stroomafwaarts wordt gestript waar een beeld op weg naar een portal langskomt. Hoe het eruitziet bepaal jij: stampStyle kiest tussen dark, light, minimal en banner, stampScale schaalt hem tussen 0.7 en 1.6, en stampLang rendert hem in een van elf talen.
Wat je niet bepaalt is het faalgedrag. Als het label wordt gevraagd en niet kan worden aangebracht, wordt het beeld achtergehouden in plaats van ongelabeld geleverd. De call antwoordt 500 DISCLOSURE_STAMP_FAILED en je credit wordt terugbetaald. Er is geen vlag om dat uit te zetten, en geen standaardinstelling per key die het stilletjes uitschakelt. Het alternatief is een ongelabelde gestylede foto sturen naar een klant die er expliciet om een label vroeg, en dat is de ene bug in dit systeem die iemand voor een toezichthouder kan brengen.
Fouten, en de ene regel daarover
Elke fout antwoordt met een JSON-body die zowel een error-string als een code bevat. Vertak op de code, nooit op het bericht. Berichten worden herschreven; codes niet.
Degene die je in productie echt tegenkomt zijn 402 INSUFFICIENT_CREDITS (bijladen; de body bevat het resterende saldo), 409 CONCURRENCY_LIMIT (te veel renders tegelijk op één key; de standaardruimte is drie tegelijk, dus wacht een seconde en probeer opnieuw), 409 REQUEST_IN_FLIGHT (je hebt een idempotency key opnieuw geprobeerd die nog loopt, dus wacht in plaats van door te drukken), 429 RATE_LIMITED, en 422 NO_IMAGE_GENERATED, wat betekent dat het model niets teruggaf en wat automatisch wordt terugbetaald. De renderlimiet is momenteel 60 per key per vijf minuten; heb je meer nodig, of een factuur in plaats van een kaart, mail ons dan.
Let op het patroon in dit alles: alles wat na een afschrijving faalt, wordt terugbetaald. Die invariant is de reden dat de API synchroon is en de reden dat variations op één staat, en het is het ding waarvoor we functies zouden opgeven.
Voor wie dit is, en voor wie niet
De API bestaat voor de gevallen waarin styling een stap is in de pipeline van iemand anders in plaats van iets waar een mens voor gaat zitten:
- Woningplatforms en IDX-leveranciers die stylen bij het inlezen, zodat de lege-kamerfoto van een makelaar al ingericht is tegen de tijd dat hij in een zoekresultaat verschijnt.
- Aanlevertooling van fotografen. Heb je al een script dat een shoot hernoemt, verkleint en uploadt, dan wordt stylen daar één stap extra in plaats van een avond in een webapp.
- PropTech-producten die stylen als functie willen zonder het leidingwerk van het model, het stempelen van de vermelding en de foutafhandeling erachter zelf te bouwen.
- Batchwerk van elke omvang, omdat de prijs per beeld geldt en de ondergrens $3 is.
Hij is nadrukkelijk niet bedoeld voor iemand die één huis stylet. Ben jij dat, dan kan de webapp meer dan de API, want Masking Studio en de AI Designer hebben geen equivalent in de API, en hij is gratis, zonder watermerk. $0.15 betalen om in code te doen wat je gratis in een browser kunt doen zou een vreemde keuze zijn, en dat zeggen we liever dan dat we je een creditpakket verkopen.
Aan de slag
Maak een key aan op de pagina met API-keys, koop het kleinste pakket en besteed één credit aan een foto waarvan je de uitkomst al kent. Lees daarna de referentie goed door voordat je je batchloop schrijft, vooral de idempotentietabel. Twintig credits is $3, en dat is ruim genoeg om erachter te komen of dit in je pipeline past.
Bronnen en noten
- Stagify API-documentatie. De gezaghebbende referentie voor endpoints, parameters, foutcodes en actuele creditpakketprijzen. Waar dit artikel en die pagina elkaar tegenspreken, heeft die pagina gelijk: de prijstabel daar wordt gegenereerd uit dezelfde data waartegen de betaal-webhook afrekent, en dit is proza die op een bepaalde dag is geschreven.
- Dashboard voor API-keys en credits. Keys aanmaken en intrekken, pakketten kopen en het saldo aflezen. Alleen op desktop.
- De hier genoemde rate limits, de gelijktijdigheidsruimte per key en de pakketprijzen zijn de standaardwaarden op het moment van schrijven en kunnen veranderen. Hogere limieten en facturatie op rekening zijn op aanvraag beschikbaar via team@stagify.ai.
- Californië's AB 723 en de hierboven genoemde MLS-vermeldingsregels worden met bronvermelding behandeld in Moet je virtuele woningstyling vermelden? in plaats van hier herhaald.