Launched June 12th, 2026. Screenshot quality is improving quickly, and feedback is welcome. Send feedback

CI / CD

Screenshots that regenerate on every release.

Wire appscreenshotapi into the pipeline you already run. Captures go in from a build, fresh localized store assets come out, and upload stays an explicit release step.

GitHub Actions

Render screenshots on a tagged release, store the API key in repository secrets, and upload or commit the reviewed output.

Fastlane

Call appscreenshotapi from a lane, download the Fastlane-compatible ZIP, and point deliver at the extracted folder.

CLI and agents

Hit the same API from a local shell, build script, or coding agent that can read the docs and operate a release checklist.

Async renders

Large locale and store matrices return a render id immediately with async: true, so runners can poll status until the set is ready.

GitHub Actions: render on tagged release

Store your live key (asa_live_…) as the APPSCREENSHOTAPI_KEY repository secret. On a version tag, this workflow posts your render request, asks for a fastlane_zip archive, downloads it, and uploads the set as a build artifact for review.

# .github/workflows/screenshots.yml
# Regenerate store screenshots on every tagged release.
name: Store screenshots

on:
  push:
    tags: ["v*"]

jobs:
  render:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # 1. Render the set and request a Fastlane-ready archive.
      #    output.format=fastlane_zip adds archive.url to the response.
      - name: Render screenshots
        env:
          APPSCREENSHOTAPI_KEY: ${{ secrets.APPSCREENSHOTAPI_KEY }}
        run: |
          curl -fsS -X POST https://api.appscreenshotapi.com/v1/renders \
            -H "Authorization: Bearer $APPSCREENSHOTAPI_KEY" \
            -H "Content-Type: application/json" \
            -H "Idempotency-Key: ${{ github.ref_name }}" \
            -d @screenshots/render.json > render.json

      # 2. Pull archive.url out of the response and download the ZIP.
      - name: Download Fastlane archive
        run: |
          ARCHIVE_URL=$(jq -r '.archive.url' render.json)
          curl -fsSL "$ARCHIVE_URL" -o screenshots.zip
          unzip -o screenshots.zip -d fastlane/screenshots

      # 3. Upload the set as a build artifact for human review.
      - name: Upload for review
        uses: actions/upload-artifact@v4
        with:
          name: store-screenshots
          path: fastlane/screenshots

The request body is a plain POST /v1/renders payload checked into the repo, so screenshot changes show up in diffs and code review.

// screenshots/render.json - the POST /v1/renders request body.
{
  "preset": "aurora-midnight",
  "canvas": { "preset": "appstore.iphone_6_9" },
  "theme": {
    "accentColor": "#5EEAD4",
    "device": { "frame": { "style": "clay" }, "shadow": { "elevation": "medium" } }
  },
  "output": {
    "locales": ["en-US", "de-DE"],
    "format": "fastlane_zip"
  },
  "slides": [
    {
      "role": "hook",
      "headline": { "lines": ["Track every habit", "in one tap"], "accent": "one tap" },
      "screenshot": { "url": "https://acme.app/shots/home.png" }
    },
    {
      "role": "feature",
      "headline": { "lines": ["See your streaks build"] },
      "subtitle": "Daily, weekly, and monthly views.",
      "screenshot": { "url": "https://acme.app/shots/streaks.png" }
    }
  ]
}

Fastlane: render, then deliver

The Fastlane-compatible ZIP is live today. Set output.format to fastlane_zip, read archive.url from the response, and extract it into the folder structure deliver expects. The lane below renders the set; a separate, reviewed lane runs the upload.

# fastlane/Fastfile
# A lane that renders the set, then hands the folder to deliver.

lane :screenshots do
  # 1. Render and download the Fastlane-ready archive.
  sh("curl -fsS -X POST https://api.appscreenshotapi.com/v1/renders " \
     "-H 'Authorization: Bearer #{ENV['APPSCREENSHOTAPI_KEY']}' " \
     "-H 'Content-Type: application/json' " \
     "-d @../screenshots/render.json > ../render.json")

  archive_url = sh("jq -r '.archive.url' ../render.json").strip
  sh("curl -fsSL '#{archive_url}' -o ../screenshots.zip")
  sh("unzip -o ../screenshots.zip -d screenshots")
end

# deliver uploads the screenshots directory to App Store Connect.
# Keep store upload an explicit, reviewed step - never auto-publish.
lane :upload_screenshots do
  deliver(
    screenshots_path: "fastlane/screenshots",
    skip_binary_upload: true,
    skip_metadata: true,
    overwrite_screenshots: true,
    submit_for_review: false
  )
end

The API produces the deliver-ready folder and fastlane deliver owns the upload, so the store submission stays an explicit human step. See Fastlane-compatible ZIPs for the exact folder layout.

Workflow shape

Build real app captures, render the set with an Idempotency-Key, check the lint report, download the outputs, and send the assets through human review before store upload.

POST /v1/renders            # sync, or "async": true
GET  /v1/renders/{id}       # poll async renders
output.format=fastlane_zip  # deliver-ready archive.url

Large matrices: poll, do not block

For big locale and store matrices, send "async": true. The API returns 202 with a render id; the runner polls GET /v1/renders/{id} until the status is succeeded.

CI can poll the render id or use a signed callback endpoint to continue the pipeline when the assets are ready.