Cómo subir capturas de pantalla del App Store con la App Store Connect API
appScreenshotSet para el locale y dispositivo correctos, reservar un appScreenshot (Apple te da URLs de fragmentos pre-firmadas), hacer PUT de los bytes y luego un PATCH de la reserva con uploaded: true y el MD5 del archivo. La autenticación es un JWT de 20 minutos firmado con tu clave .p8. Nada de esto es difícil; todo es tedioso. Aquí tienes la secuencia real — y el atajo si prefieres no construirla tú mismo.La documentación de Apple sobre esto es precisa y casi ilegible: el flujo está repartido en una docena de páginas de referencia sin un solo ejemplo completo. Este post es esa guía única, en orden, con los endpoints y nombres de campo exactos. Está escrita para quien esté decidiendo si construir la integración o usar una herramienta que ya lo haga.
El modelo de objetos en el que estás subiendo
Las capturas no se adjuntan directamente a tu app. Cuelgan de una cadena de objetos, y hay que recorrerla de arriba hacia abajo:
- appStoreVersion — la versión concreta que estás editando (debe estar en un estado editable).
- appStoreVersionLocalization — uno por cada locale del App Store (en-US, de-DE, ja, etcétera).
- appScreenshotSet — uno por cada tipo de pantalla dentro de una localización (iPhone de 6,7", iPad de 13", etc.).
- appScreenshot — la imagen individual, hasta 10 por set.
Así que una ficha totalmente localizada en 50 idiomas con dos tamaños de iPhone y uno de iPad son 50 × 3 sets, cada uno con hasta 10 capturas. Esa multiplicación es la razón por la que la gente automatiza esto en lugar de hacer clic desde la interfaz web.
Paso 0 — el JWT de 20 minutos
Toda petición, salvo la propia subida de bytes, necesita una cabecera Authorization: Bearer con un JSON Web Token que firmas tú mismo. Necesitas tres cosas de App Store Connect: tu issuer ID, un key ID y el archivo de clave privada .p8 que descargas una sola vez al crear la clave (Apple no vuelve a mostrarla).
El token se firma con ES256 — ECDSA sobre la curva P-256 con SHA-256 — usando esa clave .p8. La cabecera lleva el kid; el payload lleva el issuer como iss, la audiencia como el string literal appstoreconnect-v1, y un exp que no supere los 20 minutos tras iat. Apple rechaza cualquier cosa con una vida más larga, así que hay que regenerarlo a menudo — genera un token nuevo por lote en lugar de intentar mantener uno abierto.
Fírmalo con cualquier librería JWT compatible con ES256 (jsonwebtoken, PyJWT, la que uses en tu stack), pasa el contenido del .p8 como clave, y pon el resultado en la cabecera Authorization de cada llamada de las siguientes.
Paso 1 — crear el set de capturas
Un set está limitado a una localización y un tipo de pantalla. Haces POST a /v1/appScreenshotSets con el screenshotDisplayType en attributes y una relación que apunta al appStoreVersionLocalization que quieres rellenar.
Tipos de pantalla comunes: APP_IPHONE_67 (6,7", 1290 × 2796), el tamaño actual de iPhone grande, y APP_IPAD_PRO_3GEN_129 (12,9", 2048 × 2732). Algo útil que Apple hace ahora: las capturas del iPhone de mayor tamaño se reutilizan en las clases de iPhone más pequeñas, y las del iPad más grande en los iPads más pequeños — así que en la práctica a menudo solo necesitas un set de iPhone y uno de iPad por locale, no uno por dispositivo físico. La respuesta te da un id de set que llevas a la siguiente llamada. Si ya existe un set para ese locale/tipo, reutilízalo en lugar de crear un duplicado.
Paso 2 — reservar la captura
No subes el archivo de una vez. Primero lo reservas: le dices a Apple el nombre del archivo y el tamaño exacto en bytes, y te devuelve un plan de subida. Haces POST a /v1/appScreenshots con los attributes fileName y fileSize y una relación con el set del paso 1.
La respuesta es la parte interesante. Dentro de los attributes de la nueva captura obtienes uploadOperations — un array que describe exactamente cómo enviar los bytes. Para un archivo pequeño es una sola operación; para uno grande, Apple lo divide en varias. Cada entrada da un method (PUT), una url pre-firmada, el offset y length de bytes a enviar, y las requestHeaders que hay que adjuntar.
Paso 3 — hacer PUT de los bytes
Para cada operación, lee el fragmento de tu archivo desde offset durante length bytes, y haz PUT a la url indicada con exactamente las requestHeaders que dio Apple. Estas URLs están pre-firmadas, así que no envías tu JWT aquí — añadir la cabecera Authorization puede incluso romper la petición firmada. Varias operaciones pueden ejecutarse en paralelo, pero Apple limita la velocidad de las subidas agresivas, así que envuélvelas en un retry con backoff en lugar de lanzarlas todas de golpe.
Paso 4 — confirmar con uploaded: true y el MD5
Subir los bytes no sirve de nada hasta que le dices a Apple que la reserva está completa. Haz PATCH de la captura con uploaded: true y un sourceFileChecksum — el MD5 del archivo completo como string hexadecimal en minúsculas. Esta es la comprobación de integridad de Apple: si el checksum no coincide con lo que llegó, el procesamiento falla.
Después del PATCH, el activo entra en procesamiento por parte de Apple. Consulta el assetDeliveryState de la captura hasta que alcance un estado completado — y lee sus errors si falla, porque ahí es donde salen a la luz los problemas de dimensión incorrecta y canal alfa. El PUT y el PATCH pueden salir bien y la captura puede seguir siendo rechazada minutos después durante el procesamiento. No des por hecho que un 2xx en el commit significa que has terminado; vigila el delivery state.
Paso 5 — fijar el orden de visualización
Las capturas vuelven en el orden en que se crearon, que rara vez es el orden que quieres mostrar. El orden del set es una relación aparte. Haz PATCH a /v1/appScreenshotSets/{id}/relationships/appScreenshots con un array de IDs de capturas — el orden del array es el orden de visualización en la tienda.
Las restricciones que Apple realmente aplica
- Solo PNG o JPEG. Nada de HEIC ni WebP. Si tu renderizador produce otra cosa, conviértelo antes.
- Sin canal alfa. Aplana la transparencia sobre un fondo sólido y exporta en RGB. Un canal alfa perdido es uno de los rechazos silenciosos más comunes en el procesamiento.
- Dimensiones exactas por tipo de pantalla. La imagen tiene que coincidir con precisión con el tamaño en píxeles del tipo de pantalla — nada de aproximaciones ni de escalado hacia arriba. Un archivo de 1290 × 2796 va en un set
APP_IPHONE_67y en ningún otro. - Hasta 10 por set. Diez capturas como máximo por localización y tipo de pantalla.
- Solo versión editable. Solo puedes modificar sets en una versión que esté en estado editable; una versión ya en revisión está bloqueada.
Por qué esto es más trabajo de lo que parece
Cada llamada individual es sencilla. El coste está en todo lo que las rodea: firmar y rotar un token de 20 minutos, calcular MD5s, trocear archivos según uploadOperations, gestionar reintentos de subidas parciales, consultar el delivery state, mapear tus locales a los códigos de locale de Apple, y hacer todo esto más de 150 veces para una ficha bien localizada. Dejarlo fiable es un fin de semana de autenticación, subidas por fragmentos y gestión de errores — y luego te toca mantenerlo cada vez que Apple añade un tipo de pantalla.
Si quieres la secuencia sin tener que escribirla, el deliver de Fastlane envuelve esta misma API y es el camino open source estándar. Resuelve la subida; no diseña el carrusel ni escribe los textos.
…o sáltate todo esto
La API solo mueve imágenes terminadas. Alguien todavía tiene que diseñar el carrusel, escribir los textos y traducirlos por locale — que es la parte que realmente lleva tiempo. Mokbi hace eso hoy mismo: diseña las capturas, redacta el texto de la ficha, y lo traduce todo a 50 idiomas, y luego exporta cada imagen con las dimensiones exactas por tipo de pantalla que exige esta API — PNG, RGB, sin alfa, del tamaño correcto — para que una subida no rebote en el paso del delivery state.
Después lo publica. Para el App Store, Mokbi ejecuta por debajo esta misma secuencia de reserva y confirmación — subiendo tus capturas y metadatos y dejando la versión preparada para que la envíes — así que nunca tienes que tocar un JWT ni un PUT por fragmentos. Apple sigue exigiendo el envío final y su revisión; todo lo anterior queda resuelto por ti. Para Google Play lo publica directamente en la tienda. En cualquier caso obtienes el resultado de la API sin escribir el código, con el diseño, los textos y la traducción a 50 idiomas ya hechos.