Skip to main content

GitHub Actions

How to generate App Store screenshots in GitHub Actions

Verified against the primary sources below on

Steps

  1. 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_here
    

    Never inline the key in the workflow file. A workflow file is readable by anyone who can read the repository.

  2. 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.

  3. 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."
      }'
    
  4. 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 }
        ]
      }
    }
    
  5. 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"
    done
    

    The alpha channel page covers what the error looks like when it does reach you.

  6. 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

Go deeper

The rule behind it

Related guides