El motor de home staging virtual que hay detrás de la aplicación web ya está disponible como API HTTP. Envías la foto de una habitación, indicas un tipo de estancia y un estilo de mobiliario, y la imagen amueblada vuelve en la misma respuesta. Cuesta $0.15 por imagen, no hay suscripción, ni mínimo, ni factura mensual, y la documentación de referencia está en stagify.ai/developers.html.
Esta entrada es lo que una página de referencia no puede ser: por qué la API tiene la forma que tiene, y el puñado de cosas que te harán tropezar la primera tarde.
Toda la API es un único endpoint
Hay exactamente una llamada que cuesta dinero.
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"
Esa es la integración. La respuesta lleva la imagen amueblada como data URL, junto con un estado y un id de petición. Todo lo demás en la API es gratuito y de solo lectura: GET /api/v1/options te da los valores admitidos de cada enum, GET /api/v1/me te dice si tu clave funciona, GET /api/v1/credits informa del saldo y GET /api/v1/renders/{id} recupera un render concreto.
Las peticiones son multipart/form-data, porque estás subiendo una fotografía y base64 en un cuerpo JSON te costaría un tercio más de bytes a cambio de nada. Las imágenes son JPEG, PNG o WebP de hasta 25 MB, y lo que se comprueba es el tipo MIME, no la extensión del archivo.
Por qué no hay cola de trabajos
El diseño obvio para una API de imágenes lenta es aceptar la petición, devolver un id de trabajo y hacer que el cliente consulte el resultado. Deliberadamente no lo hicimos, y vale la pena explicar por qué, porque desde fuera parece un atajo y no lo es.
Esta aplicación no tiene ninguna cola de trabajos. Añadir una solo para la API significaría introducir una cola cuyos trabajos viven en un único proceso, en un host que se reinicia con cada despliegue. Cuando eso ocurre a mitad de un render, el crédito del cliente ya está gastado y no queda ningún socket para avisarle. Ese fallo es estrictamente peor que una conexión caída, porque una conexión caída puede reintentarse con la misma clave de idempotencia y no cuesta nada.
Así que un render mantiene la conexión abierta hasta que termina, lo que lleva desde unos segundos hasta un par de minutos. Configura el read timeout de tu cliente en 300 segundos. El valor por defecto en la mayoría de bibliotecas HTTP es 30 o 60, y un cliente que se rinde a los 30 segundos parecerá una API que falla constantemente mientras gasta créditos en silencio en renders que nunca recoge.
status: "succeeded", y GET /api/v1/renders/{id} ya existe. Si algún día añadimos una cola, llegará como un nuevo valor de status en el mismo endpoint, no como una API nueva. Trata cualquier estado distinto de succeeded como «ve a consultar el GET» y tu cliente ya estará preparado para el futuro.
El mismo razonamiento limita variations a 1. Una petición es un crédito es una imagen es un reembolso, sin aritmética de reparto parcial en ninguna parte del dinero. Si quieres tres versiones de una habitación, lanza tres peticiones simultáneas: consigues así mejor paralelismo del que nunca te dio el fan-out.
Créditos prepago, y por qué eso es una medida de seguridad
Compras créditos por adelantado. Cada render correcto gasta uno, y un render que falla se reembolsa automáticamente, así que nunca pagas por una foto que no recibiste. Los packs empiezan en 20 créditos por $3, y el precio por imagen baja cuanto más grandes son, hasta $0.12 por imagen en el pack de 500 créditos. La tabla en vivo está en la página de documentación de la API, generada a partir de los mismos datos con los que cobra el webhook de pago, de modo que no puede desviarse de lo que realmente se te factura.
La facturación por consumo habría sido más fácil de construir. El prepago es mejor para ti en un aspecto concreto: una clave robada te puede costar como mucho el saldo que le quede. No hay factura a final de mes, ni excesos, ni ningún escenario en el que una clave filtrada se convierta en una factura de cuatro cifras antes de que nadie se dé cuenta. El corte en seco al llegar a cero es el objetivo, no una limitación, y por eso también cada respuesta lleva una cabecera X-Stagify-Credits-Remaining: puedes ver acercarse el muro sin tener que consultarlo.
Las claves están pensadas solo para uso de servidor a servidor. La API no envía cabeceras CORS, así que el JavaScript del navegador no puede llamarla, y eso es deliberado, no una omisión que pensemos corregir. Una clave accesible desde un navegador es una clave filtrada. Mostramos una clave nueva exactamente una vez, guardamos solo un hash de ella y la revocamos al instante desde el panel de claves de API.
Cuatro cosas que te harán tropezar
Toda API tiene comportamientos perfectamente razonables una vez explicados y desconcertantes antes de explicarlos. Aquí están los nuestros, dichos con claridad en lugar de dejarlos en una tabla de parámetros.
roomTypedistingue mayúsculas y espacios, y un valor erróneo no se rechaza. Se escribe en el prompt como texto libre, así queLiving Roomno esLiving room. Obtendrás un render, solo que no el que querías, y lo habrás pagado. El conjunto admitido esBedroom,Living room,Dining room,Kitchen,Office,Bathroom,OutdoorsyDorm. Sácalo deGET /api/v1/optionsen lugar de reescribirlo en tu propio desplegable.- Un
furnitureStyleno reconocido se convierte silenciosamente enstandard. El mismo tipo de problema, con un síntoma más callado: una errata en el nombre de un estilo no da error, simplemente te deja el aspecto por defecto en todas las imágenes del lote. removeFurnitureaceptatrueuon, y nada más. Ni1, niyes. Los dos se leen como false, y obtienes una foto amueblada con el sofá viejo todavía dentro. Su compañerokeepFurnitureadmite texto libre indicando qué dejar en su sitio, hasta 500 caracteres, y solo se lee cuandoremoveFurnitureestá realmente activado.- Envía una
Idempotency-Keyen cada petición. Omitirla está permitido y significa que no hay protección frente a repeticiones, que es el comportamiento honesto por defecto; no vamos a derivarla de tu foto, porque entonces dos renders genuinamente distintos de la misma habitación colisionarían y al segundo llamante se le entregaría la imagen del primero. Reintenta con el mismo valor y una petición completada se reproduce desde el almacenamiento conX-Stagify-Replayed: truey sin un segundo cargo. Reutiliza una con parámetros diferentes y obtienes422 IDEMPOTENCY_KEY_REUSED, que es la API diciéndote que tu generador de claves tiene un bug.
Más allá de eso, los parámetros que conviene conocer reflejan los de la aplicación web: additionalPrompt para indicaciones en texto libre, y hasta cinco fotos de referencia furnitureImage con las que el modelo pueda compararse.
La etiqueta de divulgación no se puede desactivar
La mayoría de las normas de la MLS, y la guía de la NAR que hay detrás de ellas, exigen que una foto de anuncio amueblada digitalmente indique que lo está; desde enero de 2026, la AB 723 de California lo exige por ley. Hemos escrito la versión estado por estado de eso con detalle.
labelVirtuallyStaged graba la divulgación en los píxeles en lugar de adjuntarla como metadatos, porque los metadatos los elimina prácticamente cualquier herramienta por la que pase una imagen camino de un portal. Tú controlas su aspecto: stampStyle elige entre dark, light, minimal y banner, stampScale lo dimensiona entre 0.7 y 1.6, y stampLang lo representa en cualquiera de once idiomas.
Lo que no controlas es el modo de fallo. Si se pide la etiqueta y no se puede aplicar, la imagen se retiene en lugar de entregarse sin etiquetar. La llamada responde 500 DISCLOSURE_STAMP_FAILED y tu crédito se reembolsa. No hay ninguna opción para desactivar eso, ni un valor por defecto por clave que lo apague sin hacer ruido. La alternativa es enviar una foto amueblada sin etiquetar a un cliente que pidió explícitamente que se etiquetara, que es el único bug de este sistema capaz de poner a alguien delante de un regulador.
Errores, y la única regla sobre ellos
Todo fallo responde con un cuerpo JSON que lleva tanto una cadena error como un code. Ramifica según el código, nunca según el mensaje. Los mensajes se reescriben; los códigos no.
Los que te encontrarás de verdad en producción son 402 INSUFFICIENT_CREDITS (recarga; el cuerpo lleva el saldo restante), 409 CONCURRENCY_LIMIT (demasiados renders en curso con una misma clave; el margen por defecto es de tres a la vez, así que espera un segundo y reintenta), 409 REQUEST_IN_FLIGHT (has reintentado una clave de idempotencia que sigue en ejecución, así que espera en lugar de insistir), 429 RATE_LIMITED y 422 NO_IMAGE_GENERATED, que significa que el modelo no devolvió nada y se reembolsa automáticamente. El límite de renders es actualmente de 60 por clave cada cinco minutos; si necesitas más, o una factura en lugar de una tarjeta, escríbenos.
Fíjate en el patrón común a todos ellos: todo lo que falla después de un cargo se reembolsa. Ese invariante es la razón de que la API sea síncrona y de que variations esté limitado a uno, y es lo que mantendríamos aunque hubiera que renunciar a funcionalidades.
Para quién es esto, y para quién no
La API existe para los casos en los que el staging es un paso dentro del pipeline de otro y no algo que una persona se sienta a hacer:
- Portales de anuncios y proveedores de IDX que amueblan en la ingesta, de modo que la foto de una habitación vacía de un agente ya llegue amueblada a la cuadrícula de resultados.
- Herramientas de entrega de fotógrafos. Si ya tienes un script que renombra, redimensiona y sube una sesión, el staging pasa a ser un paso más dentro de él en lugar de una tarde en una aplicación web.
- Productos PropTech que quieren el staging como funcionalidad sin construir la fontanería del modelo, el sellado de la divulgación y la gestión de errores que hay detrás.
- Trabajo por lotes de cualquier tamaño, porque el precio es por imagen y el suelo son $3.
No es en absoluto para una persona que amuebla una sola casa. Si ese eres tú, la aplicación web hace más que la API, ya que Masking Studio y el AI Designer no tienen equivalente en la API, y es gratis y sin marca de agua. Pagar $0.15 por hacer en código lo que puedes hacer gratis en un navegador sería una elección extraña, y preferimos decírtelo a venderte un pack de créditos.
Primeros pasos
Crea una clave en la página de claves de API, compra el pack más pequeño y gasta un crédito en una foto cuyo resultado ya conozcas. Luego lee bien la referencia antes de escribir el bucle por lotes, sobre todo la tabla de idempotencia. Veinte créditos son $3, y con eso sobra para averiguar si esto encaja en tu pipeline.
Fuentes y notas
- Documentación de la API de Stagify. La referencia autorizada sobre endpoints, parámetros, códigos de error y precios actuales de los packs de créditos. Cuando este artículo y esa página discrepen, esa página tiene razón: su tabla de precios se genera a partir de los mismos datos con los que cobra el webhook de pago, y este artículo es prosa escrita un día concreto.
- Panel de claves de API y créditos. Crea y revoca claves, compra packs y consulta el saldo. Solo para escritorio.
- Los límites de uso, el margen de concurrencia por clave y los precios de los packs citados aquí son los valores por defecto en el momento de escribir esto y están sujetos a cambios. Hay límites más altos y facturación por factura disponibles a petición en team@stagify.ai.
- La AB 723 de California y las normas de divulgación de la MLS mencionadas arriba se tratan con citas en ¿Hay que declarar el home staging virtual? en lugar de repetirse aquí.