# 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.
- URL: https://socialcutter.theboomer.dev/guias/etsy/
- Idioma: es
- Familia: cms
- Actualizado: 2026-09-24
- Palabras clave: 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 listing | Destino SocialCutter | Medida generada |
|---|---|---|
| Vista cuadrada, foto principal en 1:1 | `instagram` `post` | 1080x1080 (1:1) |
| Foto secundaria apaisada | `twitter` `post` | 1200x675 (16:9) |
| Banda ancha para una cabecera o una ficha | `linkedin` `post` | 1200x627 (1.91:1) |
| Vertical para un vídeo corto del listing | `instagram` `story` | 1080x1920 (9:16) |
| Imagen muy ancha, tipo banner | `youtube` `banner` | 2560x1440 (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:

| Criterio | Qué pide Etsy |
|---|---|
| Formatos aceptados | `.jpg`, `.gif`, `.png`, `.svg` y `.heic` |
| Formatos no soportados | GIF animado y PNG con transparencia; las zonas transparentes se ven negras |
| Tamaño recomendado del listing | Ancho y alto de al menos 2000 píxeles |
| Primera foto | Al menos 635 píxeles de ancho y de alto, o el listing baja posiciones en las búsquedas |
| Peso del fichero | Por encima de 1 MB la subida puede no completarse, sobre todo con mala conexión |
| Perfil de color | Etsy convierte a sRGB: parte de sRGB para no llevarte sorpresas |
| Primera foto | Horizontal (apaisada) o cuadrada |
| Vistas | Etsy 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:

```json
{
  "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

```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

```bash
# 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 "image=@listing-1.jpg;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ódigo | Origen | Qué pasa | Qué hacer |
|---|---|---|---|
| 400 | Etsy | Problema con los datos de la petición | Revisar el multipart y los campos enviados |
| 401 | Etsy | Faltan las credenciales o el token no vale | Comprobar `x-api-key` y el `Authorization` Bearer |
| 403 | Etsy | La operación no está permitida para ese token | Añadir el scope `listings_w` y volver a autorizar |
| 404 | Etsy | No encuentra el recurso | Revisar `shop_id` y `listing_id` |
| 409 | Etsy | Conflicto con el estado actual del listing | Releer las posiciones antes de reescribir |
| 500 | Etsy | Error interno de Etsy | Reintentar con espera; la imagen no se ha creado |
| La subida no termina | Etsy | El fichero pasa de 1 MB, sobre todo con conexión lenta | Bajar `options.quality` y volver a generar el JPG |
| 401 | SocialCutter | Falta o no vale la cabecera `X-API-Key` | Comprobar que el valor empieza por `sc_` y sigue activo |
| 413 | SocialCutter | El maestro supera 5 MB | Reducir la imagen antes de la llamada |
| 429 | SocialCutter | Cuota del monedero agotada | Consultar `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

- Otras tiendas: [Integra SocialCutter con la API de Shopify](/guias/shopify/), [WooCommerce](/guias/woocommerce/) y [BigCommerce](/guias/bigcommerce/)
- Medidas: [Medidas de redes sociales: tamaños y proporciones](/guias/medidas-redes-sociales/)
- Formatos: [PNG, JPG o WebP: qué formato usar en cada red](/guias/formatos-redes-sociales/)
- Código: [Automatiza SocialCutter con Python](/guias/python/) y [desde la terminal con curl](/guias/curl/)
- Documentación de la API de SocialCutter: https://docs.socialcutter.theboomer.dev
- Referencia de la API v3 de Etsy: https://developer.etsy.com/documentation/reference/
- Requisitos de imagen de Etsy: https://help.etsy.com/hc/en-us/articles/115015663347