Cómo funciona la API de renderizado de Screenshots Live — Guía práctica para automatizar listings de tiendas
Un recorrido técnico de cómo la API de renderizado de Screenshots Live procesa templates, aplica overrides YAML y genera capturas listas para la tienda — con ejemplos reales de código y patrones CI/CD para configuraciones whitelabel.
Qué cubre este post
Si mantienes apps whitelabel — o incluso una sola app con múltiples listings en tiendas — probablemente ya te topaste con el problema de las capturas de pantalla. Apple quiere capturas para cada tamaño de dispositivo. Google Play quiere su propio set. Soporta tres idiomas y triplicaste el trabajo. Ahora multiplica eso por la cantidad de variantes de marca que mantienes.
El post anterior explicó por qué existe Screenshots Live. Este es la continuación práctica: cómo está diseñada la API de renderizado, qué espera y cómo conectarla a tu pipeline de build para que las capturas dejen de ser trabajo manual.
Cómo está diseñado el pipeline de renderizado
El sistema de renderizado no es un endpoint sincrónico de "mandás request, recibís imagen". Es un pipeline basado en jobs, lo cual importa cuando estás renderizando docenas o cientos de capturas en lote.
El flujo:
- Enviás un request de renderizado con un ID de template y overrides YAML opcionales (texto, capturas, device frames — cualquier cosa marcada como intercambiable).
- Validación — el sistema verifica que tu template existe, que te pertenece y que cada campo que querés sobreescribir está efectivamente permitido. Las URLs se validan contra rangos de IP privados y protocolos restringidos.
- Cola — el job entra en una cola BullMQ respaldada por Redis. Tu llamada API retorna inmediatamente con un
jobIdy estadoPending. Este es el punto clave: no esperás a que termine el renderizado. - Renderizado — un worker basado en Rust toma el job, carga el template e imágenes (con caché LRU para renders repetidos), carga fuentes, renderiza el canvas usando Skia y genera la salida.
- Empaquetado — las imágenes renderizadas se comprimen en un ZIP y se suben al object storage.
- Descarga — hacés polling al endpoint de estado. Cuando está
Completed, pedís al endpoint de descarga una URL prefirmada (válida por 1 hora).
El diseño asíncrono es intencional. Cuando necesitás renderizar 50 templates para un release, los disparás todos y recolectás los resultados. Sin bloqueos, sin timeouts.
Autenticación
Necesitás una cuenta Pro tier para acceder a la API de renderizado. Creá un API key desde tu dashboard — se ve como sa_live_.... Cada request lo usa como Bearer token:
Authorization: Bearer sa_live_your_key_here
Templates: Cómo están estructurados
Todo gira alrededor de los templates. Un template es un canvas que armás en el editor visual — colocás bloques de texto, device frames, imágenes y fondos. Cada elemento en el canvas es un item con un UUID único.
Para la automatización, el concepto crítico son los campos intercambiables (swappable fields). No toda propiedad de cada item se puede sobreescribir por API. Un admin configura qué campos son intercambiables — contenido de texto, familia tipográfica, tamaño de fuente, colores, URLs de capturas, IDs de device frames, etc. Es una restricción deliberada: evita que los consumidores de la API rompan el layout accidentalmente cambiando posiciones o tamaños que el diseñador dejó fijos.
Pensalo como un contrato entre tu diseñador y tu pipeline: el diseñador es dueño del layout, el pipeline es dueño del contenido.
El sistema de overrides YAML
Acá es donde se pone interesante para la automatización. En vez de renderizar un template exactamente como fue guardado, enviás YAML que sobreescribe campos específicos de items específicos.
Paso 1: Obtener el scaffold YAML
Cada template tiene un endpoint scaffold que devuelve todos los campos intercambiables como YAML comentado:
curl -H "Authorization: Bearer sa_live_your_key_here" \
https://api.screenshots.live/templates/TU_TEMPLATE_ID/yaml
Vas a recibir algo así:
templateId: "550e8400-e29b-41d4-a716-446655440000"
items:
- itemId: "7c9e6679-7425-40de-944b-e07fc1f90ae7"
type: Text
# text: "Tu título acá"
# fontFamily: "Inter"
# fontSize: 48
# color: "#FFFFFF"
- itemId: "a1b2c3d4-5678-90ab-cdef-1234567890ab"
type: DeviceFrame
# screenshotUrl: "https://..."
# frameId: "..."
Descomentá lo que querés sobreescribir. Dejá el resto — esos campos mantienen los valores por defecto del template.
Paso 2: Enviar el render
curl -X POST https://api.screenshots.live/render/api \
-H "Authorization: Bearer sa_live_your_key_here" \
-H "Content-Type: text/yaml" \
-d '
templateId: "550e8400-e29b-41d4-a716-446655440000"
items:
- itemId: "7c9e6679-7425-40de-944b-e07fc1f90ae7"
type: Text
text: "Rastrea tus entregas"
color: "#1A73E8"
- itemId: "a1b2c3d4-5678-90ab-cdef-1234567890ab"
type: DeviceFrame
screenshotUrl: "https://your-cdn.com/screenshots/home-screen.png"
'
Paso 3: Consultar el estado
curl -H "Authorization: Bearer sa_live_your_key_here" \
https://api.screenshots.live/render/api/JOB_ID
El estado progresa: Pending → Active → Completed (o Failed). La mayoría de los templates se renderizan en segundos.
Paso 4: Descargar
curl -H "Authorization: Bearer sa_live_your_key_here" \
https://api.screenshots.live/render/JOB_ID/download
Devuelve una URL prefirmada. La descarga es un ZIP con las imágenes renderizadas.
Subir capturas directamente
No necesitás hostear tus capturas en algún lado solo para pasar una URL. El endpoint /render/api/with-pictures acepta uploads multipart:
curl -X POST https://api.screenshots.live/render/api/with-pictures \
-H "Authorization: Bearer sa_live_your_key_here" \
-F 'yaml=templateId: "TU_TEMPLATE_ID"
items:
- itemId: "DEVICE_FRAME_ITEM_ID"
type: DeviceFrame
screenshotUrl: "picture://home-screen.png"' \
-F 'pictures=@./screenshots/home-screen.png'
El protocolo picture:// referencia archivos subidos por nombre. Podés adjuntar hasta 200 archivos de 10MB cada uno. Esto es lo que hace la API práctica para CI/CD — tu pipeline captura screenshots y los mandás directo al renderizado sin almacenamiento intermedio.
Ejemplo real: Pipeline whitelabel
Un escenario concreto: mantenés 5 marcas whitelabel, cada una necesita capturas de App Store en 3 idiomas para 5 pantallas clave. Son 75 imágenes renderizadas por release.
Tu diseñador creó un template por pantalla en el editor, con el texto del headline y el device frame marcados como intercambiables. Tu pipeline CI (GitLab, GitHub Actions, lo que sea) captura screenshots crudos por marca e idioma con snapshot/screengrab de Fastlane.
El script que une todo:
#!/bin/bash
API_KEY="sa_live_your_key_here"
API_BASE="https://api.screenshots.live"
BRANDS=("acme" "globex" "initech" "umbrella" "stark")
LOCALES=("en" "de" "es")
SCREENS=("home" "profile" "search" "settings" "checkout")
declare -A TEMPLATES
TEMPLATES[home]="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
TEMPLATES[profile]="aaaaaaaa-bbbb-cccc-dddd-ffffffffffff"
declare -A HEADLINES
HEADLINES[home:en]="Track your deliveries"
HEADLINES[home:de]="Verfolge deine Lieferungen"
HEADLINES[home:es]="Rastrea tus entregas"
JOB_IDS=()
for brand in "${BRANDS[@]}"; do
for locale in "${LOCALES[@]}"; do
for screen in "${SCREENS[@]}"; do
HEADLINE="${HEADLINES[$screen:$locale]}"
BRAND_COLOR=$(cat "brands/$brand/primary-color.txt")
RESPONSE=$(curl -s -X POST "$API_BASE/render/api/with-pictures" \
-H "Authorization: Bearer $API_KEY" \
-F "yaml=templateId: \"${TEMPLATES[$screen]}\"
items:
- itemId: \"TEXT_ITEM_UUID\"
type: Text
text: \"$HEADLINE\"
color: \"$BRAND_COLOR\"
- itemId: \"FRAME_ITEM_UUID\"
type: DeviceFrame
screenshotUrl: \"picture://${screen}.png\"" \
-F "pictures=@./artifacts/$brand/$locale/${screen}.png")
JOB_ID=$(echo "$RESPONSE" | jq -r '.data.jobId')
JOB_IDS+=("$JOB_ID|$brand|$locale|$screen")
done
done
done
echo "${#JOB_IDS[@]} jobs enviados. Esperando resultados..."
for entry in "${JOB_IDS[@]}"; do
IFS='|' read -r job_id brand locale screen <<< "$entry"
while true; do
STATUS=$(curl -s -H "Authorization: Bearer $API_KEY" \
"$API_BASE/render/api/$job_id" | jq -r '.status')
if [ "$STATUS" = "Completed" ]; then
URL=$(curl -s -H "Authorization: Bearer $API_KEY" \
"$API_BASE/render/$job_id/download" | jq -r '.downloadUrl')
mkdir -p "./output/$brand/$locale"
curl -s -o "./output/$brand/$locale/${screen}.zip" "$URL"
echo "Listo: $brand/$locale/$screen"
break
elif [ "$STATUS" = "Failed" ]; then
echo "ERROR: $brand/$locale/$screen"
break
fi
sleep 2
done
done
75 render jobs, todos disparados en paralelo, luego polleados y descargados. En un pipeline real agregarías retry logic y polling paralelo, pero el patrón queda claro. Metelo en tu .gitlab-ci.yml o GitHub Actions y todo el flujo — desde el push de código hasta capturas listas para la tienda — corre sin intervención humana.
Cambiar device frames
Los device frames son los overlays de mockup — iPhone 16 Pro, Pixel 9, iPad Air, Galaxy S24. Están precargados en tu cuenta y se referencian por UUID. El mismo template puede renderizarse con diferentes frames:
items:
- itemId: "FRAME_ITEM_UUID"
type: DeviceFrame
screenshotUrl: "picture://home.png"
frameId: "IPHONE_16_PRO_FRAME_UUID"
Así resolvés la división App Store vs Google Play: mismo layout de template, misma captura, distinto frame. Dos render jobs — uno con el frame de iPhone, otro con el de Pixel — y cubriste ambas tiendas con un solo template.
Rate limits y cuotas
Números para planificar:
- API de renderizado: 5 requests por 60 segundos
- Polling de estado: 60 requests por 60 segundos
- Renders diarios: 100 por día en Pro tier
- Uploads de imágenes: máx 200 archivos por request, 10MB cada uno
Para setups whitelabel grandes, el rate limit en submissions de render es el cuello de botella. Agregá pequeñas pausas entre lotes. El diseño async ayuda — las submissions son rápidas, el renderizado real pasa en segundo plano.
Validación YAML
El parser YAML es deliberadamente estricto: sin anclas, sin alias, sin tags custom, máximo 1MB. Si algo está mal, el mensaje de error te dice exactamente qué — nombre de campo incorrecto, campo no intercambiable, formato UUID inválido, URL apuntando a un rango de IP privado. Diseñado para fallar rápido y claro — eso importa cuando estás debuggeando un pipeline CI a las 11 de la noche.
¿Y el spec OpenAPI?
Si preferís generar un cliente tipado en vez de escribir comandos curl, el spec OpenAPI completo está disponible en:
https://api.screenshots.live/render/openapi.json
Y hay una Swagger UI interactiva en:
https://api.screenshots.live/render/docs
Ambos requieren autenticación Pro tier. El spec cubre todos los endpoints de render, DTOs y respuestas de error — pasalo a openapi-generator u orval y tenés un cliente tipado en TypeScript, Python, Go o lo que uses.
Conclusión
La API de renderizado está construida para un workflow específico: tu diseñador crea templates en el editor visual, y tu código los llena con el contenido correcto en tiempo de build. Para setups whitelabel donde el mismo template tiene que funcionar para docenas de marcas e idiomas, el sistema de overrides YAML significa que mantenés un template por layout de pantalla en vez de uno por combinación marca-idioma-dispositivo.
Si ya tenés las capturas crudas resueltas (Fastlane, Maestro, capturas manuales — da igual) y necesitás automatizar el paso de procesamiento, para eso está Screenshots Live. El pipeline captura screenshots, la API los renderiza en imágenes listas para la tienda, y Fastlane los sube. Sin diseñador en el loop para releases rutinarios.