Saltar al contenido principal
SocialCutter

CMS y webs

Sube a Etsy las imágenes del listing con la API v3

Genera los formatos con SocialCutter y súbelos al listing por la API v3 de Etsy en multipart: requisitos de tamaño y formato, OAuth, jpg o png y errores.

  • Etsy
  • API v3
  • listing images
  • listings_w
  • multipart
  • Bearer
  • jpg

Por qué generar las proporciones antes de subir

Etsy recorta tus fotos para sus propias vistas: la miniatura cuadrada, la vertical y la apaisada. Su página de requisitos lo dice sin rodeos: conviene que la imagen tenga margen suficiente para poder recortarse a cuadrado, vertical y horizontal sin perder producto, y que el motivo importante esté en el centro.

Si subes un solo maestro, el recorte lo decide Etsy y tú no ves el resultado hasta después. Si generas antes la proporción exacta del hueco al que va la imagen, el fichero ya encaja y el recorte no tiene nada que quitar. El recorte de SocialCutter es centrado: escala y recorta el exceso por igual a los dos lados, sin analizar el contenido de la imagen.

Hueco en el listingDestino SocialCutterMedida generada
Vista cuadrada, foto principal en 1:1instagram post1080x1080 (1:1)
Foto secundaria apaisadatwitter post1200x675 (16:9)
Banda ancha para una cabecera o una fichalinkedin post1200x627 (1.91:1)
Vertical para un vídeo corto del listinginstagram story1080x1920 (9:16)
Imagen muy ancha, tipo banneryoutube banner2560x1440 (16:9)

Dos avisos honestos sobre las medidas:

  • No hay 4:5 en el catálogo. Las verticales son 9:16 (1080x1920) y la cuadrada es 1:1 (1080x1080). Si necesitas exactamente 4:5, tendrás que recortarla fuera de SocialCutter.
  • La medida la fija el destino y no se amplía. Si Etsy recomienda 2000 píxeles de ancho y alto, el cuadrado del catálogo (1080x1080) se queda por debajo de esa recomendación aunque supere con holgura el mínimo de 635 píxeles de la primera foto. Cuando el zoom a 2000 píxeles sea un requisito, sube la foto principal a resolución completa y usa SocialCutter para las medidas del resto de canales y para las fotos secundarias.

Requisitos de imagen de Etsy

Lo que publica su centro de ayuda:

CriterioQué pide Etsy
Formatos aceptados.jpg, .gif, .png, .svg y .heic
Formatos no soportadosGIF animado y PNG con transparencia; las zonas transparentes se ven negras
Tamaño recomendado del listingAncho y alto de al menos 2000 píxeles
Primera fotoAl menos 635 píxeles de ancho y de alto, o el listing baja posiciones en las búsquedas
Peso del ficheroPor encima de 1 MB la subida puede no completarse, sobre todo con mala conexión
Perfil de colorEtsy convierte a sRGB: parte de sRGB para no llevarte sorpresas
Primera fotoHorizontal (apaisada) o cuadrada
VistasEtsy recorta a cuadrado, vertical y horizontal, así que la imagen necesita margen

La consecuencia práctica para el flujo es directa: pide JPG o PNG, no WebP. Etsy no lista WebP entre los formatos que acepta, y la salida por defecto de SocialCutter es WebP. En la misma petición se fija el formato y la calidad:

{
  "source": { "type": "url", "value": "https://tu-cdn.com/maestro.jpg" },
  "destinations": [ { "platform": "instagram", "format": "post" } ],
  "options": { "fit_mode": "cover", "format": "jpg", "quality": 88 }
}

quality va de 1 a 100 y por defecto es 85. Bajarla es la palanca más rápida para quedarte por debajo del megabyte que Etsy considera seguro; en una foto de producto, un JPG a 85-90 suele pesar bastante menos de 1 MB sin que se note.

La subida por la API v3

POST https://openapi.etsy.com/v3/application/shops/{shop_id}/listings/{listing_id}/images
  • Cuerpo: multipart/form-data, con el fichero en el campo image.
  • Campos opcionales:
    • rank: posición en el listing; la 1 es la que aparece más a la izquierda. Por defecto 1.
    • overwrite: en true, reemplaza la imagen que ya ocupa ese rank. Por defecto false.
    • alt_text: texto alternativo, máximo 500 caracteres.
    • listing_image_id: reasigna una imagen ya borrada en lugar de subir una nueva.
    • is_watermarked: marca de agua, por defecto false.
  • Autenticación: dos cosas a la vez. La cabecera x-api-key con el formato keystring:shared_secret y la cabecera Authorization: Bearer <token>.
  • Permiso: el token OAuth necesita el scope listings_w.
  • Respuesta: 201 con un objeto de imagen del listing que trae listing_image_id, rank, alt_text, full_width, full_height y url_fullxfull (hasta 3000 píxeles por lado).

Si mandas image y listing_image_id en la misma petición, la API sube la del campo image e ignora el identificador.

Lee con atención la nota de la referencia: al subir una imagen nueva, los datos calculados (colores, medidas) pueden volver a null porque Etsy la procesa de forma asíncrona. Hay que consultarlos después con el endpoint de consulta de la imagen del listing.

OAuth de Etsy

  • Autorización: https://www.etsy.com/oauth/connect.
  • Token: https://openapi.etsy.com/v3/public/oauth/token.
  • Base de la API: https://openapi.etsy.com, con las rutas bajo /v3/application/.

El token caduca y hay que renovarlo; el refresh_token del flujo de código de autorización es lo que permite hacerlo sin volver a pasar por la pantalla de consentimiento. Guarda el keystring, el shared secret y el token en variables de entorno, nunca en el código.

Snippet completo en Python

import json, os, requests

API = "https://api.socialcutter.theboomer.dev"
ETSY = "https://openapi.etsy.com/v3/application"
SHOP_ID = os.environ["ETSY_SHOP_ID"]
LISTING_ID = os.environ["ETSY_LISTING_ID"]

sc = requests.Session()
sc.headers["X-API-Key"] = os.environ["SOCIALCUTTER_API_KEY"]

etsy = requests.Session()
etsy.headers["x-api-key"] = os.environ["ETSY_API_KEY"]          # keystring:shared_secret
etsy.headers["Authorization"] = f"Bearer {os.environ['ETSY_ACCESS_TOKEN']}"

# 1. Formatos en jpg: WebP no está entre los que acepta Etsy
r = sc.post(f"{API}/api/v1/images/process", json={
    "source": {"type": "url", "value": "https://tu-cdn.com/maestro.jpg"},
    "destinations": [
        {"platform": "instagram", "format": "post"},    # 1080x1080 (1:1)
        {"platform": "twitter", "format": "post"},      # 1200x675 (16:9)
    ],
    "options": {"fit_mode": "cover", "format": "jpg", "quality": 88},
}, timeout=120)
r.raise_for_status()

# 2. Subir cada salida al listing con su rank
for puesto, salida in enumerate(r.json()["outputs"], start=1):
    img = sc.get(salida["url"], timeout=120)
    img.raise_for_status()

    nombre = f"{salida['platform']}-{salida['format']}.jpg"
    up = etsy.post(
        f"{ETSY}/shops/{SHOP_ID}/listings/{LISTING_ID}/images",
        files={"image": (nombre, img.content, "image/jpeg")},
        data={
            "rank": puesto,
            "overwrite": "true",
            "alt_text": "Camiseta de algodón, vista frontal",
        },
        timeout=120,
    )
    up.raise_for_status()
    datos = up.json()
    print(datos["listing_image_id"], datos["rank"], datos["url_fullxfull"])

Snippet con curl

# 1. Generar los formatos en jpg
curl -s -X POST "https://api.socialcutter.theboomer.dev/api/v1/images/process" \
  -H "X-API-Key: $SOCIALCUTTER_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{"source":{"type":"url","value":"https://tu-cdn.com/maestro.jpg"},
           "destinations":[{"platform":"instagram","format":"post"}],
           "options":{"fit_mode":"cover","format":"jpg","quality":88}}' \
  > salida.json

curl -s "$(jq -r '.outputs[0].url' salida.json)" -o listing-1.jpg

# 2. Subirla al listing en multipart
curl -s -X POST \
  "https://openapi.etsy.com/v3/application/shops/$ETSY_SHOP_ID/listings/$ETSY_LISTING_ID/images" \
  -H "x-api-key: $ETSY_API_KEY" \
  -H "Authorization: Bearer $ETSY_ACCESS_TOKEN" \
  -F "[email protected];type=image/jpeg" \
  -F "rank=1" \
  -F "overwrite=true" \
  -F "alt_text=Camiseta de algodón, vista frontal" \
  | jq '{listing_image_id, rank, url_fullxfull}'

Para llenar varias posiciones, repite la segunda llamada cambiando rank (2, 3, 4…) y el fichero. El campo overwrite solo hace falta cuando quieres sustituir lo que ya está en esa posición.

Coste

  • 1 uso por destino (plataforma y formato) por petición a SocialCutter. Destinos repetidos no se cobran dos veces y los procesamientos fallidos se devuelven.
  • Planes: 0 EUR (3 usos/día), 3 EUR (10/día), 9 EUR (30/día) y 29 EUR (100/día), todos con API y servidor MCP incluidos.
  • La API de Etsy no se cobra por llamada; aplica sus propios límites de uso, así que espacia las subidas si vas a llenar muchas posiciones seguidas.

Errores típicos

CódigoOrigenQué pasaQué hacer
400EtsyProblema con los datos de la peticiónRevisar el multipart y los campos enviados
401EtsyFaltan las credenciales o el token no valeComprobar x-api-key y el Authorization Bearer
403EtsyLa operación no está permitida para ese tokenAñadir el scope listings_w y volver a autorizar
404EtsyNo encuentra el recursoRevisar shop_id y listing_id
409EtsyConflicto con el estado actual del listingReleer las posiciones antes de reescribir
500EtsyError interno de EtsyReintentar con espera; la imagen no se ha creado
La subida no terminaEtsyEl fichero pasa de 1 MB, sobre todo con conexión lentaBajar options.quality y volver a generar el JPG
401SocialCutterFalta o no vale la cabecera X-API-KeyComprobar que el valor empieza por sc_ y sigue activo
413SocialCutterEl maestro supera 5 MBReducir la imagen antes de la llamada
429SocialCutterCuota del monedero agotadaConsultar GET /api/v1/wallet

Lo que SocialCutter no hace

El recorte es centrado y determinista: no analiza el contenido de la imagen para decidir qué conservar, no edita la foto (no retoca color, no quita fondos, no compone texto), no publica en redes sociales ni en Etsy, y no acepta ficheros de más de 5 MB. Genera las versiones con la medida exacta de cada destino y devuelve sus URLs: subirlas al listing es cosa de tu script.

Siguientes pasos

Preguntas frecuentes

¿Qué formatos de fichero acepta Etsy?

Su página de requisitos lista .jpg, .gif, .png, .svg y .heic. WebP no está en esa lista, así que la salida por defecto de SocialCutter (webp) no vale aquí: pide options.format con jpg o png en la misma petición.

¿Qué tamaño pide Etsy para la foto de un listing?

Etsy recomienda un ancho y un alto de al menos 2000 píxeles, y pide que la primera foto tenga como mínimo 635 píxeles de ancho y de alto para no perder posiciones en las búsquedas. El catálogo de SocialCutter fija la medida de cada destino y no amplía, así que la medida sale de la lista de destinos y no al revés.

¿Por qué me devuelve null el color o el tamaño de la imagen recién subida?

Porque Etsy procesa la imagen de forma asíncrona. La propia referencia de la API avisa de que campos como los colores o el tamaño pueden llegar a null en la respuesta de subida; se leen después con el endpoint de consulta de la imagen del listing.

¿Cómo sustituyo una foto que ya está en una posición del listing?

Con rank para indicar la posición y overwrite en true, que reemplaza la imagen que ya ocupa ese puesto. Si no lo pones, Etsy añade una imagen nueva y la respuesta no sustituye nada.

¿Cuánto cuesta preparar los formatos de un listing?

1 uso por destino, es decir por cada pareja de plataforma y formato. Dos medidas para el mismo listing son 2 usos. Los planes incluyen la API y el servidor MCP.