CMS y webs
Contentful: cuándo pre-generar imágenes y cómo publicarlas
La Images API de Contentful ya recorta al vuelo: mira cuándo conviene pre-generar los ficheros con SocialCutter y cómo publicar el asset por la Management API.
- Contentful
- Images API
- Content Management API
- assets
- uploadFrom
- imágenes para redes sociales
- pre-generar medidas
Lo que Contentful ya hace por su cuenta
Contentful tiene su propia Images API: una API de solo lectura servida desde images.ctfassets.net a la que se le añaden parámetros de consulta a la URL del fichero (fields.file.url) para transformar la imagen al vuelo. No hay que subir nada nuevo ni generar ficheros: cambias la URL y el CDN devuelve la versión transformada.
| Parámetro | Qué hace | Valores |
|---|---|---|
w / h | Ancho y alto en píxeles | máximo 4000 px |
fit | Comportamiento del ajuste | pad, fill, scale, crop, thumb |
f | Foco del encuadre cuando usas pad, fill, crop o thumb | el valor por defecto es center |
fm | Formato de salida | jpg, png, webp, gif, avif, tiff; por defecto, el original |
q | Calidad | entero de 1 a 100 |
bg | Color de fondo del pad y del redondeo | valores RGB, por ejemplo rgb:9090ff |
r | Esquinas redondeadas o recorte circular | píxeles, o max |
fl | Variantes concretas | progressive para JPEG, png8 para PNG de 8 bits |
Un ejemplo de URL de salida:
https://images.ctfassets.net/SPACE_ID/ASSET_ID/TOKEN/nombre.jpg?w=1080&h=1080&fit=fill&fm=webp&q=85
Los assets publicados no necesitan autenticación en la Images API, así que cualquiera puede pedir esas transformaciones desde tu web. Con fit=fill y el foco por defecto (center) obtienes un recorte centrado equivalente al de SocialCutter. Y si la imagen original supera los 100 MB, Contentful la trata como asset y no aplica transformaciones.
Conclusión honesta: para tu web y tu blog, casi nunca necesitas pre-generar nada. Una sola llamada bien formada desde la plantilla resuelve el problema. Pre-generar tiene sentido en otro escenario.
Dónde se queda corta para redes sociales
La Images API devuelve una URL transformada. No devuelve un fichero descargable preparado para subir. Eso no es un defecto: es que resuelve otro problema.
Cuando publicas en Instagram, TikTok, YouTube o LinkedIn, la red no se descarga tu URL: tú le subes un fichero. Y los programadores de publicaciones, las herramientas de campañas, los partners y los dosieres de cliente casi siempre piden un fichero con su nombre y su peso. Ahí no hay parámetro que valga.
Cuándo merece la pena pre-generar con SocialCutter
| Caso | Por qué | Destino habitual |
|---|---|---|
| Piezas para redes que no pasan por el CDN de Contentful | La red necesita un fichero con la medida exacta, no una URL | instagram post, instagram story, tiktok cover |
| Programadores de publicaciones y herramientas de campañas | Solo aceptan una subida de fichero | facebook post, linkedin post, twitter post |
| Campañas fuera de Contentful (pago, email, partners) | El material sale del CMS y se entrega en mano | youtube thumbnail, twitter header |
| Exportaciones y entregas a cliente | Hace falta un paquete de ficheros con nombres legibles y un peso controlado | Todos los de la campaña |
| Un maestro que alimenta varios canales | Un solo diseño, una salida por canal, sin tocar la plantilla | linkedin cover, facebook cover |
| Partners que no pueden usar URLs con parámetros | Su sistema no construye la transformación | Cualquiera |
El flujo es siempre el mismo: entra un maestro, SocialCutter devuelve cada medida y el fichero se publica o se entrega. El recorte de cover, el modo por defecto, es centrado: escala y recorta el sobrante a partes iguales por los dos lados, sin analizar la imagen. Deja aire en los bordes del maestro.
Cuándo NO hace falta pre-generar
| Caso | Qué usar en su lugar |
|---|---|
| La imagen solo se sirve desde tu web o tu blog | Los parámetros de la Images API (w, h, fit, fm, q); no consume usos |
| El tema o la plantilla ya aplica su proporción | Nada: no dupliques assets |
| Solo quieres distintas resoluciones para distintas pantallas | El mismo asset con w+fit y un srcset |
| Archivo histórico de la marca | Guarda el maestro sin recortar y genera al publicar |
| Necesitas retoque, fondo transparente o texto compuesto | Un editor de fotos: no es lo que hace SocialCutter |
Un aviso que evita un problema caro: no sustituyas el maestro de Contentful por una salida recortada. Si el asset principal de la campaña pasa a ser un 1080x1080, el banner de YouTube de 2560x1440 saldrá ampliado y borroso. El maestro se queda como maestro; las medidas se generan en el momento de publicar.
Generar las medidas de una campaña
curl -s -X POST "https://api.socialcutter.theboomer.dev/api/v1/images/process" \
-H "X-API-Key: sc_tu_clave" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: contentful-campana-2026-09" \
-d '{
"source": { "type": "url", "value": "https://images.ctfassets.net/SPACE_ID/ASSET_ID/TOKEN/maestra.jpg" },
"destinations": [
{ "platform": "instagram", "format": "post" },
{ "platform": "instagram", "format": "story" },
{ "platform": "linkedin", "format": "post" },
{ "platform": "youtube", "format": "thumbnail" }
],
"options": { "fit_mode": "cover", "format": "jpg", "quality": 85 }
}' > sc.json
jq -r '.outputs[] | "\(.platform)/\(.format) \(.width)x\(.height) \(.size_bytes) bytes \(.url)"' sc.json
Esa petición consume 4 usos y devuelve cuatro salidas. Fíjate en el detalle útil: puedes usar como source la URL del propio CDN de Contentful, así que el maestro sigue viviendo en un único sitio. Cada salida es pública, que es justo lo que necesitas para publicarla en tu red o subirla como asset.
Publicar el asset con la Content Management API
La Content Management API (CMA) usa la base https://api.contentful.com y autenticación Authorization: Bearer <token>. Todas las llamadas llevan Content-Type: application/vnd.contentful.management.v1+json. Crear un asset son tres pasos: crear, procesar y publicar.
export CF="https://api.contentful.com/spaces/SPACE_ID/environments/ENV_ID"
export CF_TOKEN="CFPAT-..."
export LOCALE="es-ES"
1. Sube el binario a la Upload API
Si el fichero ya está en una URL pública (por ejemplo, una salida de SocialCutter), puedes saltar este paso y dar la URL directamente en el campo upload al crear el asset. Si lo tienes en local, súbelo primero:
curl -s -X POST "https://upload.contentful.com/spaces/SPACE_ID/environments/ENV_ID/uploads" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary @instagram-post.jpg > upload.json
UPLOAD_ID=$(jq -r '.sys.id' upload.json)
La respuesta trae el sys.id del upload. Ojo con su caducidad: si no lo asocias a un asset y lo procesas en 24 horas, el fichero y sus metadatos se borran. Los clientes con residencia de datos en la UE usan upload.eu.contentful.com.
2. Crea el asset apuntando al upload
curl -s -X POST "$CF/assets" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/vnd.contentful.management.v1+json" \
-d '{
"fields": {
"title": { "es-ES": "Campaña septiembre · Instagram post 1080x1080" },
"file": {
"es-ES": {
"contentType": "image/jpeg",
"fileName": "campana-septiembre-instagram-post-1080x1080.jpg",
"uploadFrom": {
"sys": { "type": "Link", "linkType": "Upload", "id": "'"$UPLOAD_ID"'" }
}
}
}
}
}' > asset.json
ASSET_ID=$(jq -r '.sys.id' asset.json)
VERSION=$(jq -r '.sys.version' asset.json)
Para elegir tú el ID, usa un PUT a /spaces/SPACE_ID/environments/ENV_ID/assets/ASSET_ID: crea el asset con ese ID o actualiza el que ya existe. En la actualización, Contentful no fusiona cambios: se envía el recurso completo y hay que mandar la versión actual en la cabecera X-Contentful-Version (bloqueo optimista).
El nombre del fichero tiene reglas: solo letras, cifras, puntos, guiones y guiones bajos; cualquier otro carácter se sustituye por un guion bajo. Mejor no confiar en acentos ni símbolos.
3. Procesa y publica
# Procesar: obligatorio antes de publicar
curl -s -o /dev/null -w "%{http_code}\n" -X PUT \
"$CF/assets/$ASSET_ID/files/$LOCALE/process" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/vnd.contentful.management.v1+json" \
-H "X-Contentful-Version: $VERSION"
# Publicar
curl -s -X PUT "$CF/assets/$ASSET_ID/published" \
-H "Authorization: Bearer $CF_TOKEN" \
-H "Content-Type: application/vnd.contentful.management.v1+json" \
-H "X-Contentful-Version: $VERSION" | jq '{id: .sys.id, publishedVersion: .sys.publishedVersion}'
Procesar es el paso que trae el fichero al sistema de Contentful y rellena fields.file.url; la llamada puede volver antes de que termine el procesamiento. Sin procesar no se puede publicar ni previsualizar el asset en la pestaña Media. Al publicar, el asset queda disponible en la Content Delivery API y, si es una imagen, en images.ctfassets.net con los parámetros de transformación.
Con el token de la CMA el límite por defecto es de 7 peticiones por segundo; si te pasas, la API responde 429 y te dice cuánto esperar en X-Contentful-RateLimit-Reset.
El mismo flujo en Python
import requests
CF = "https://api.contentful.com/spaces/SPACE_ID/environments/ENV_ID"
HDR = {"Authorization": "Bearer CFPAT-...",
"Content-Type": "application/vnd.contentful.management.v1+json"}
LOCALE = "es-ES"
salidas = requests.post(
"https://api.socialcutter.theboomer.dev/api/v1/images/process",
headers={"X-API-Key": "sc_...", "Content-Type": "application/json"},
json={"source": {"type": "url", "value": "https://images.ctfassets.net/.../maestra.jpg"},
"destinations": [{"platform": "instagram", "format": "post"},
{"platform": "youtube", "format": "thumbnail"}]},
timeout=60,
).json()["outputs"]
for out in salidas:
binario = requests.get(out["url"], timeout=60).content
upload = requests.post(
"https://upload.contentful.com/spaces/SPACE_ID/environments/ENV_ID/uploads",
headers={"Authorization": HDR["Authorization"],
"Content-Type": "application/octet-stream"},
data=binario, timeout=120,
).json()["sys"]["id"]
nombre = f"campana-{out['platform']}-{out['format']}-{out['width']}x{out['height']}.jpg"
asset = requests.post(f"{CF}/assets", headers=HDR, json={"fields": {
"title": {LOCALE: f"Campaña {out['platform']} {out['format']}"},
"file": {LOCALE: {"contentType": "image/jpeg", "fileName": nombre,
"uploadFrom": {"sys": {"type": "Link", "linkType": "Upload", "id": upload}}}}},
}, timeout=60).json()
ver = asset["sys"]["version"]
requests.put(f"{CF}/assets/{asset['sys']['id']}/files/{LOCALE}/process",
headers={**HDR, "X-Contentful-Version": str(ver)}, timeout=60).raise_for_status()
requests.put(f"{CF}/assets/{asset['sys']['id']}/published",
headers={**HDR, "X-Contentful-Version": str(ver)}, timeout=60).raise_for_status()
print("Publicados", len(salidas), "assets")
Coste
- 1 uso por destino (plataforma y formato) por petición; los repetidos no se cobran dos veces.
- Las transformaciones de la Images API de Contentful no consumen usos: son de Contentful.
- Los procesamientos fallidos se devuelven.
- Todos los planes incluyen API y MCP: Free 3 usos/día, Basic 10, Pro 30, Agency 100, desde 0 / 3 / 9 / 29 EUR al mes.
Errores típicos
| Síntoma | Causa | Qué hacer |
|---|---|---|
401 en el CMA | Token ausente, caducado o sin permiso sobre el entorno | Revisa el personal access token y su acceso al entorno |
409 / conflicto de versión | X-Contentful-Version desactualizado | Vuelve a leer el asset y repite con su versión actual |
| “Cannot publish until processing” | Se intentó publicar sin procesar | Procesa primero el fichero del locale |
| El asset queda en borrador y sin URL | El upload caducó antes de procesarse | Vuelve a subir el binario: el upload expira en 24 horas |
422 con caracteres raros en el nombre | fileName con acentos o símbolos | Usa solo letras, cifras, puntos, guiones y guiones bajos |
429 en el CMA | Más de 7 peticiones por segundo | Espera lo que indique X-Contentful-RateLimit-Reset |
El Content-Type vuelve con error | Falta la cabecera de versión de la API | Envía application/vnd.contentful.management.v1+json en cada llamada |
| Imagen borrosa en el banner de YouTube | Se guardó una salida recortada como maestro | Conserva el maestro y genera el banner desde él |
413 en SocialCutter | El maestro supera los 5 MB | Reduce el maestro o usa la URL del CDN como source |
Siguientes pasos
- Medidas: Medidas de redes sociales: tamaños y proporciones
- Formatos: PNG, JPG o WebP: qué formato usar en cada red
- Automatización: Automatizar imágenes para redes sociales: los 4 caminos reales
- Webflow: Sube imágenes a Webflow y usa SocialCutter
- WordPress: Integra SocialCutter con WordPress y WooCommerce
- Referencia de la Management API: https://www.contentful.com/developers/docs/references/content-management-api/
- Referencia de la Images API: https://www.contentful.com/developers/docs/references/images-api/
Preguntas frecuentes
Si Contentful ya redimensiona en la URL, ¿para qué quiero SocialCutter?
Para todo lo que no pasa por su CDN. La Images API devuelve una URL transformada, no un fichero: cuando la red social, el programador de publicaciones, el partner o la entrega al cliente exige un fichero con la medida exacta, ese fichero hay que generarlo aparte.
¿Debo sustituir el maestro de Contentful por la versión recortada?
No. Es un error. Si guardas un 1080x1080 como asset principal, no podrás sacar el banner de YouTube de 2560x1440 sin ampliarlo. Guarda el maestro sin recortar y genera las medidas al publicar.
¿Cuántas llamadas hacen falta para publicar un asset por la API?
Tres: crear el asset, procesarlo y publicarlo. Si el fichero no está en una URL accesible, antes hay una cuarta llamada: subir el binario a la Upload API para obtener un upload_id. Procesar es obligatorio: sin procesar no se puede publicar.
¿Qué va en la cabecera Content-Type de la Management API?
application/vnd.contentful.management.v1+json en las llamadas del CMA, con Authorization: Bearer <token>. Ese Content-Type es el que fija la versión de la API, así que conviene enviarlo siempre en cada llamada en lugar de dejarlo al valor por defecto.
¿Qué cuesta preparar una campaña con SocialCutter?
1 uso por destino, es decir por cada pareja de plataforma y formato. Los destinos repetidos en la misma petición no se cobran dos veces y los procesamientos fallidos se devuelven. Las transformaciones de la Images API de Contentful no consumen usos.
¿Puedo publicar el asset con un ID que yo elija?
Sí. Además del POST que genera el ID automáticamente, la Management API permite crear o actualizar un asset con un ID propio con un PUT a /spaces/{space_id}/environments/{environment_id}/assets/{asset_id}. Es útil para que el ID coincida con el de la campaña.