App Store-screenshots uploaden met de App Store Connect API
appScreenshotSet aan voor de juiste locale en het juiste device, reserveer een appScreenshot (Apple geeft je pre-signed chunk-URL's terug), PUT de bytes, en PATCH daarna de reservering met uploaded: true en de MD5 van het bestand. Auth is een 20 minuten geldige JWT, ondertekend met je .p8-key. Niets ervan is moeilijk; alles ervan is prutswerk. Hier is de echte sequentie — en de kortere weg als je 'm liever niet zelf bouwt.Apple's documentatie hierover is accuraat en bijna onleesbaar — de flow staat verspreid over een dozijn referentiepagina's zonder één doorlopend voorbeeld. Deze post is die ene walkthrough, op volgorde, met de exacte endpoints en veldnamen. Geschreven voor wie moet beslissen: de integratie zelf bouwen, of een tool pakken die het al heeft.
Het objectmodel waar je in uploadt
Screenshots hangen niet direct aan je app. Ze hangen aan een keten van objecten, en die loop je van boven naar beneden af:
- appStoreVersion — de specifieke versie die je bewerkt (moet in een bewerkbare staat staan).
- appStoreVersionLocalization — één per App Store-locale (en-US, de-DE, ja, enzovoort).
- appScreenshotSet — één per schermtype binnen een localization (6,7" iPhone, 13" iPad, enz.).
- appScreenshot — de individuele afbeelding, tot 10 per set.
Een volledig gelokaliseerde listing in 50 talen, over twee iPhone-formaten en één iPad-formaat, is dus 50 × 3 sets, elk met tot 10 screenshots. Die vermenigvuldiging is precies de reden waarom mensen dit automatiseren in plaats van door de webinterface te klikken.
Stap 0 — de 20-minuten-JWT
Elke request, behalve de bytes-uploads zelf, heeft een Authorization: Bearer-header nodig met een JSON Web Token dat je zelf ondertekent. Je hebt drie dingen nodig uit App Store Connect: je issuer ID, een key ID, en het .p8-privésleutelbestand dat je één keer downloadt bij het aanmaken van de key (Apple laat 'm daarna nooit meer zien).
Het token wordt ondertekend met ES256 — ECDSA op de P-256-curve met SHA-256 — met die .p8-key. De header bevat de kid; de payload bevat de issuer als iss, de audience als de letterlijke string appstoreconnect-v1, en een exp van maximaal 20 minuten na iat. Apple weigert alles met een langere levensduur, dus je regenereert vaak — genereer per batch een vers token in plaats van er één open te houden.
Onderteken dat met elke ES256-compatibele JWT-library (jsonwebtoken, PyJWT, wat je stack ook gebruikt), geef de .p8-inhoud mee als key, en zet het resultaat in de Authorization-header bij elke call hieronder.
Stap 1 — maak de screenshot-set aan
Een set hoort bij één localization en één schermtype. Je POST't naar /v1/appScreenshotSets met de screenshotDisplayType in attributes en een relationship die verwijst naar de appStoreVersionLocalization die je wilt vullen.
Gangbare schermtypes: APP_IPHONE_67 (6,7", 1290 × 2796), het huidige grote iPhone-formaat, en APP_IPAD_PRO_3GEN_129 (12,9", 2048 × 2732). Één handige zaak die Apple nu doet: screenshots voor het grootste iPhone-formaat worden hergebruikt voor de kleinere iPhone-klassen, en het grootste iPad-formaat voor de kleinere iPads — dus in de praktijk heb je vaak maar één iPhone-set en één iPad-set per locale nodig, niet één per fysiek device. De response geeft je een set-id die je meeneemt naar de volgende call. Bestaat er al een set voor die locale/dat type, hergebruik die dan in plaats van een duplicaat aan te maken.
Stap 2 — reserveer de screenshot
Je uploadt het bestand niet in één keer. Eerst reserveer je het: je vertelt Apple de bestandsnaam en exacte bytegrootte, en het geeft een uploadplan terug. POST naar /v1/appScreenshots met de attributes fileName en fileSize en een relationship naar de set uit stap 1.
De response is waar het interessant wordt. In de attributes van de nieuwe screenshot krijg je uploadOperations — een array die precies beschrijft hoe je de bytes moet pushen. Voor een klein bestand is het één operatie; voor een groot bestand splitst Apple het op in meerdere. Elk item geeft je een method (PUT), een pre-signed url, de byte-offset en length die je moet sturen, en de requestHeaders die je moet meesturen.
Stap 3 — PUT de bytes
Lees voor elke operatie het stukje van je bestand vanaf offset voor length bytes, en PUT het naar de opgegeven url met precies de requestHeaders die Apple meegaf. Deze URL's zijn pre-signed, dus je stuurt hier geen JWT mee — het toevoegen van de Authorization-header kan de ondertekende request juist kapotmaken. Meerdere operaties kunnen parallel lopen, maar Apple rate-limit agressieve uploads, dus wikkel ze in een retry-with-backoff in plaats van alles tegelijk af te vuren.
Stap 4 — commit met uploaded: true en de MD5
De bytes uploaden doet niets totdat je Apple vertelt dat de reservering compleet is. PATCH de screenshot terug met uploaded: true en een sourceFileChecksum — de MD5 van het hele bestand als lowercase hex-string. Dit is Apple's integriteitscheck: klopt de checksum niet met wat er is aangekomen, dan mislukt de verwerking.
Na de PATCH gaat het bestand aan de kant van Apple in verwerking. Poll de assetDeliveryState van de screenshot totdat die een afgeronde staat bereikt — en lees de errors als het misgaat, want daar komen problemen met verkeerde afmetingen en alphakanalen naar boven. De PUT en PATCH kunnen allebei slagen terwijl de screenshot minuten later tijdens verwerking alsnog wordt afgewezen. Ga er niet vanuit dat een 2xx op de commit betekent dat je klaar bent; kijk naar de delivery state.
Stap 5 — stel de weergavevolgorde in
Screenshots komen terug in de volgorde waarin ze zijn aangemaakt, wat zelden de volgorde is die je wilt tonen. De volgorde van een set is een aparte relationship. PATCH /v1/appScreenshotSets/{id}/relationships/appScreenshots met een array van screenshot-ID's — de volgorde in de array is de weergavevolgorde in de store.
De eisen die Apple echt afdwingt
- Alleen PNG of JPEG. Geen HEIC, geen WebP. Rendert jouw tool iets anders, converteer dan eerst.
- Geen alphakanaal. Vlak transparantie af naar een effen achtergrond en exporteer RGB. Een verdwaald alphakanaal is een van de meest voorkomende stille afwijzingen tijdens verwerking.
- Exacte afmetingen per schermtype. De afbeelding moet precies overeenkomen met de pixelgrootte van het schermtype — geen paar pixels ernaast, geen upscalen. Een bestand van 1290 × 2796 hoort in een
APP_IPHONE_67-set en nergens anders. - Tot 10 per set. Maximaal tien screenshots per localization per schermtype.
- Alleen bewerkbare versie. Je kunt sets alleen wijzigen op een versie die in een bewerkbare staat staat; een versie die al in review is, is vergrendeld.
Waarom dit meer werk is dan het lijkt
Elke individuele call is simpel. De kosten zitten in alles eromheen: een 20-minuten-token ondertekenen en roteren, MD5's berekenen, bestanden opdelen om aan uploadOperations te voldoen, retries bij gedeeltelijke uploads afhandelen, de delivery state pollen, jouw locales mappen naar Apple's locale-codes, en dat alles 150-plus keer doen voor een fatsoenlijk gelokaliseerde listing. Het betrouwbaar krijgen is een weekend aan auth, chunked uploads en foutafhandeling — en daarna is het aan jou om te onderhouden, elke keer dat Apple een nieuw schermtype toevoegt.
Wil je de sequentie zonder 'm zelf te schrijven, dan wikkelt Fastlane's deliver dezelfde API in en is het de standaard open-source route. Het lost de upload op; het tekent de screenshots niet en schrijft de teksten niet.
…of sla het allemaal over
De API verplaatst alleen kant-en-klare afbeeldingen. Iets moet nog steeds de carrousel ontwerpen, de teksten schrijven en ze per locale vertalen — en dat is het deel dat écht tijd kost. Mokbi doet dat vandaag al: het ontwerpt de screenshots, schrijft de listingtekst, en vertaalt het geheel naar 50 talen, en exporteert dan elke afbeelding op precies de per-schermtype afmetingen die deze API eist — PNG, RGB, geen alpha, juiste grootte — zodat een upload niet vastloopt bij de delivery-state-stap.
Daarna publiceert het. Voor de App Store draait Mokbi onder de motorkap exact deze reserve-en-commit-sequentie — het uploadt je screenshots en metadata en zet de versie klaar om in te dienen, zonder dat jij ooit een JWT of chunked PUT aanraakt. Apple vereist nog altijd de uiteindelijke Submit en zijn review; alles daarvoor is voor je geregeld. Voor Google Play zet het direct door naar de store. In beide gevallen krijg je het resultaat van de API zonder de code te schrijven, met het ontwerp, de tekst en de 50-talenvertaling al gedaan.