GitHub Actions
How to generate App Store screenshots in GitHub Actions
Verified against the primary sources below on
Steps
Create an API key and store it as a secret
Generate a key from your account, then add it under Settings, Secrets and variables, Actions as
APPSCREENSHOT_API_KEY.Every request authenticates with a bearer token against
https://appscreenshotstudio.com/api/v1:Authorization: Bearer sk_live_your_key_hereNever inline the key in the workflow file. A workflow file is readable by anyone who can read the repository.
Create a project
A project holds the device target and the canvas. Create one per run, or reuse a fixed project id if you want the same canvas updated each time.
curl -sS -X POST https://appscreenshotstudio.com/api/v1/projects \ -H "Authorization: Bearer $APPSCREENSHOT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "device_id": "iphone-6.9", "name": "Release screenshots" }'The response carries the project id under
data.id.Generate the set from a description
The chat endpoint takes natural language and composes the cards. This is the step that costs credits, so keep it to one call per run rather than one per card.
curl -sS -X POST "https://appscreenshotstudio.com/api/v1/projects/$PROJECT_ID/chat" \ -H "Authorization: Bearer $APPSCREENSHOT_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "message": "A habit tracking app for people who keep abandoning streaks. Dark UI, emerald accents. Five cards: the streak view, adding a habit, the weekly chart, reminders, and the widget." }'Render and collect the files
Rendering costs no credits. The response returns one entry per card with a URL and its pixel size.
curl -sS -X POST "https://appscreenshotstudio.com/api/v1/projects/$PROJECT_ID/render" \ -H "Authorization: Bearer $APPSCREENSHOT_API_KEY" \ | jq -r '.data.images[].url' \ | xargs -n1 -I{} curl -sS -O {}The shape it returns:
{ "success": true, "data": { "images": [ { "card_index": 0, "url": "https://...png", "width": 1260, "height": 2736 } ] } }Flatten before these files reach App Store Connect
The render endpoint returns PNG. A PNG written from a canvas carries an alpha channel even when every pixel is opaque, and App Store Connect refuses files that carry one.
Add a flattening step in the job rather than discovering it at submission:
sudo apt-get install -y imagemagick for f in *.png; do magick "$f" -background white -alpha remove -alpha off -quality 95 "${f%.png}.jpg" doneThe alpha channel page covers what the error looks like when it does reach you.
Publish them as artifacts
Putting the whole thing together as a workflow:
name: Screenshots on: workflow_dispatch: jobs: generate: runs-on: ubuntu-latest env: API: https://appscreenshotstudio.com/api/v1 KEY: ${{ secrets.APPSCREENSHOT_API_KEY }} steps: - name: Create project run: | PROJECT_ID=$(curl -sS -X POST "$API/projects" \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -d '{"device_id":"iphone-6.9","name":"CI run"}' | jq -r '.data.id') echo "PROJECT_ID=$PROJECT_ID" >> "$GITHUB_ENV" - name: Generate run: | curl -sS -X POST "$API/projects/$PROJECT_ID/chat" \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -d '{"message":"A habit tracking app. Dark UI, emerald accents. Five cards."}' - name: Render and download run: | curl -sS -X POST "$API/projects/$PROJECT_ID/render" \ -H "Authorization: Bearer $KEY" \ | jq -r '.data.images[].url' | xargs -n1 -I{} curl -sS -O {} - name: Flatten for App Store Connect run: | sudo apt-get install -y imagemagick for f in *.png; do magick "$f" -background white -alpha remove -alpha off -quality 95 "${f%.png}.jpg" done - uses: actions/upload-artifact@v4 with: name: app-store-screenshots path: "*.jpg"
When this is worth doing
Screenshots are the one release asset that usually stays manual. The binary is built in CI, the version is bumped in CI, the release notes are templated, and then someone opens a design tool and exports ten images by hand.
They stay manual because the traditional automation options both cost real engineering. UI-test-driven capture means writing and maintaining test code whose only job is to navigate to the right screen. A design-tool export means a human in the loop by definition.
Driving generation over an API is the third option, and it is worth reaching for when the set changes often: a rebrand, a new locale, a device slot you did not previously target. For a single annual release, doing it in the builder by hand is less work than maintaining a workflow.
The credit costs are worth knowing before you put this on a schedule rather than workflow_dispatch. A chat message costs 5 credits and rendering costs none, so a run is cheap, but a nightly job that regenerates a set nobody looks at is not.
Sources
- Builder API documentation | AppScreenshotStudio (primary)
- Screenshot specifications | Apple, App Store Connect Help (primary)
Go deeper
The rule behind it
Related guides
- How to fix "Images can't include alpha channels or transparencies" in App Store ConnectThe alpha channel is a property of the file, not of anything you can see, so an image with no visible transparency still carries it. Re-encode the file as JPEG, or strip the channel from the PNG with ImageMagick. Renaming the extension does not change the bytes and produces a different error.
- How to make App Store screenshots for an Expo appEAS builds and submits your binary, and EAS Metadata pushes your store listing, but Expo documents screenshot upload as unsupported in EAS Metadata. Screenshots stay a separate step you handle in App Store Connect or a third-party tool, and an Expo project needs two sets because it ships to both stores.