GitHub Actions Screenshot API Integration

Automate website screenshots in GitHub Actions with ScreenshotAPI. Visual regression testing, deploy previews, and CI screenshot workflows.

Last updated: 2026-03-25

Try ScreenshotAPI free

5 free credits. No credit card required.

Start for free

Automate Screenshots in GitHub Actions with ScreenshotAPI

Automated screenshots in CI pipelines serve two critical purposes: visual regression testing to catch UI bugs before they reach production, and deployment evidence to document what your site looks like after each release. Traditional approaches require installing Chromium on the GitHub Actions runner, which adds 2-3 minutes to your workflow and consumes 500+ MB of disk space.

A GitHub Actions screenshot integration with ScreenshotAPI replaces that entire setup with simple HTTP requests. No browser installation, no Playwright setup, no runner image customization. Your workflow stays fast and lightweight.

Quick Start

  1. Sign up for ScreenshotAPI and copy your API key. 5 free credits are included.
  2. Add the API key as a GitHub Actions secret.
  3. Create a workflow that captures screenshots.

Installation

Add your API key as a repository secret:

  1. Go to your repository Settings > Secrets and variables > Actions.
  2. Click "New repository secret".
  3. Name it SCREENSHOTAPI_KEY and paste your key.

No action marketplace packages are required. The workflows use curl or a simple Node.js script.

Basic Example: Screenshot on Deploy

Capture screenshots after every deployment to main:

yaml
# .github/workflows/screenshot.yml name: Capture Screenshots on: push: branches: [main] jobs: screenshot: runs-on: ubuntu-latest steps: - name: Capture homepage screenshot run: | curl -s -o homepage.png \ -H "x-api-key: ${{ secrets.SCREENSHOTAPI_KEY }}" \ "https://screenshotapi.to/api/v1/screenshot?url=https://yoursite.com&width=1440&height=900&type=png" - name: Capture pricing page screenshot run: | curl -s -o pricing.png \ -H "x-api-key: ${{ secrets.SCREENSHOTAPI_KEY }}" \ "https://screenshotapi.to/api/v1/screenshot?url=https://yoursite.com/pricing&width=1440&height=900&type=png" - name: Upload screenshots as artifacts uses: actions/upload-artifact@v4 with: name: screenshots path: "*.png" retention-days: 30

Visual Regression Testing Workflow

Compare screenshots between the base branch and the PR to detect visual changes:

yaml
# .github/workflows/visual-regression.yml name: Visual Regression Test on: pull_request: branches: [main] jobs: visual-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20 - name: Capture screenshots env: SCREENSHOTAPI_KEY: ${{ secrets.SCREENSHOTAPI_KEY }} PREVIEW_URL: ${{ github.event.deployment_status.target_url || 'https://staging.yoursite.com' }} run: node .github/scripts/capture-screenshots.js - name: Compare with baselines run: node .github/scripts/compare-screenshots.js - name: Upload diff artifacts if: always() uses: actions/upload-artifact@v4 with: name: visual-diffs path: screenshots/ retention-days: 14

Screenshot Capture Script

javascript
// .github/scripts/capture-screenshots.js const fs = require('node:fs') const path = require('node:path') const API_BASE = 'https://screenshotapi.to/api/v1/screenshot' const API_KEY = process.env.SCREENSHOTAPI_KEY const BASE_URL = process.env.PREVIEW_URL || 'https://staging.yoursite.com' const pages = [ { name: 'homepage', path: '/' }, { name: 'pricing', path: '/pricing' }, { name: 'docs', path: '/docs' }, { name: 'dashboard', path: '/dashboard' }, { name: 'blog', path: '/blog' }, ] const outputDir = 'screenshots/current' fs.mkdirSync(outputDir, { recursive: true }) async function captureScreenshot(name, pagePath) { const url = `${BASE_URL}${pagePath}` const params = new URLSearchParams({ url, width: '1440', height: '900', type: 'png', waitUntil: 'networkidle', }) const response = await fetch(`${API_BASE}?${params}`, { headers: { 'x-api-key': API_KEY }, }) if (!response.ok) { console.error(`Failed to capture ${name}: ${response.status}`) return false } const buffer = Buffer.from(await response.arrayBuffer()) fs.writeFileSync(path.join(outputDir, `${name}.png`), buffer) console.log(`Captured ${name} (${buffer.length} bytes)`) return true } async function main() { const results = await Promise.allSettled( pages.map(({ name, path }) => captureScreenshot(name, path)) ) const failures = results.filter((r) => r.status === 'rejected') if (failures.length > 0) { console.error(`${failures.length} screenshots failed`) process.exit(1) } } main()

Screenshot Comparison Script

javascript
// .github/scripts/compare-screenshots.js const fs = require('node:fs') const path = require('node:path') const { execSync } = require('node:child_process') const currentDir = 'screenshots/current' const baselineDir = 'screenshots/baseline' const diffDir = 'screenshots/diff' fs.mkdirSync(diffDir, { recursive: true }) if (!fs.existsSync(baselineDir)) { console.log('No baseline found. Saving current screenshots as baseline.') fs.cpSync(currentDir, baselineDir, { recursive: true }) process.exit(0) } const currentFiles = fs.readdirSync(currentDir).filter((f) => f.endsWith('.png')) let hasChanges = false for (const file of currentFiles) { const currentPath = path.join(currentDir, file) const baselinePath = path.join(baselineDir, file) const diffPath = path.join(diffDir, file) if (!fs.existsSync(baselinePath)) { console.log(`New page detected: ${file}`) hasChanges = true continue } const current = fs.readFileSync(currentPath) const baseline = fs.readFileSync(baselinePath) if (!current.equals(baseline)) { console.log(`Visual change detected: ${file}`) hasChanges = true fs.copyFileSync(currentPath, diffPath) } else { console.log(`No changes: ${file}`) } } if (hasChanges) { console.log('\nVisual changes detected. Review the diff artifacts.') process.exit(1) } console.log('\nNo visual changes detected.')

Multi-Page Screenshot Matrix

Capture screenshots across multiple viewports and themes:

yaml
# .github/workflows/multi-viewport.yml name: Multi-Viewport Screenshots on: workflow_dispatch: schedule: - cron: '0 6 * * 1' jobs: screenshot: runs-on: ubuntu-latest strategy: matrix: viewport: - { name: desktop, width: 1440, height: 900 } - { name: tablet, width: 768, height: 1024 } - { name: mobile, width: 390, height: 844 } theme: [light, dark] steps: - name: Capture screenshot run: | curl -s -o "${{ matrix.viewport.name }}-${{ matrix.theme }}.png" \ -H "x-api-key: ${{ secrets.SCREENSHOTAPI_KEY }}" \ "https://screenshotapi.to/api/v1/screenshot?url=https://yoursite.com&width=${{ matrix.viewport.width }}&height=${{ matrix.viewport.height }}&type=png&colorScheme=${{ matrix.theme }}" - name: Upload artifact uses: actions/upload-artifact@v4 with: name: screenshot-${{ matrix.viewport.name }}-${{ matrix.theme }} path: "*.png"

PR Comment with Screenshot Preview

Post screenshot previews directly on pull requests:

yaml
# .github/workflows/pr-screenshots.yml name: PR Screenshot Preview on: pull_request: types: [opened, synchronize] jobs: preview: runs-on: ubuntu-latest permissions: pull-requests: write steps: - name: Wait for preview deployment id: wait run: | echo "Waiting for preview deployment..." sleep 60 echo "preview_url=https://preview-${{ github.event.pull_request.number }}.yoursite.com" >> $GITHUB_OUTPUT - name: Capture preview screenshot run: | curl -s -o preview.png \ -H "x-api-key: ${{ secrets.SCREENSHOTAPI_KEY }}" \ "https://screenshotapi.to/api/v1/screenshot?url=${{ steps.wait.outputs.preview_url }}&width=1440&height=900&type=png&waitUntil=networkidle" - name: Upload screenshot uses: actions/upload-artifact@v4 with: name: preview-screenshot path: preview.png - name: Comment on PR uses: peter-evans/create-or-update-comment@v4 with: issue-number: ${{ github.event.pull_request.number }} body: | ## Screenshot Preview Preview URL: ${{ steps.wait.outputs.preview_url }} Screenshot captured at $(date -u '+%Y-%m-%d %H:%M UTC'). Download the full screenshot from the [workflow artifacts](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}).

Scheduled Monitoring

Monitor your production site on a schedule:

yaml
name: Site Monitor on: schedule: - cron: '0 */4 * * *' jobs: monitor: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Capture monitoring screenshots env: SCREENSHOTAPI_KEY: ${{ secrets.SCREENSHOTAPI_KEY }} run: | mkdir -p screenshots TIMESTAMP=$(date +%Y%m%d-%H%M) for page in "" "/pricing" "/docs" "/blog"; do name=$(echo "$page" | sed 's/^\///' | sed 's/^$/homepage/') curl -s -o "screenshots/${name}-${TIMESTAMP}.png" \ -H "x-api-key: ${SCREENSHOTAPI_KEY}" \ "https://screenshotapi.to/api/v1/screenshot?url=https://yoursite.com${page}&width=1440&height=900&type=png" done - name: Upload monitoring screenshots uses: actions/upload-artifact@v4 with: name: monitor-${{ github.run_number }} path: screenshots/ retention-days: 90

Production Tips

Secrets Management

Never hardcode API keys in workflow files. Always use GitHub Actions secrets. For organization-wide access, use organization-level secrets.

Parallelism

Use matrix strategies to capture multiple screenshots in parallel. Each matrix job runs independently, so capturing 10 pages across 3 viewports results in 30 parallel jobs.

Artifact Retention

Set retention days based on your needs. Visual regression baselines should be stored in the repository (committed) rather than as artifacts, so they persist across runs.

Cost Planning

Calculate your expected credit usage: pages per run multiplied by runs per month. A workflow capturing 5 pages on 100 PRs per month uses 500 credits. The Starter plan at $20 covers this. Visit the pricing page for all options.

Further Reading

Frequently asked questions

Do I need to install Chrome in my GitHub Actions runner?

No. ScreenshotAPI handles browser rendering remotely. Your workflow only needs curl or a lightweight script to make HTTP requests.

Can I use screenshots for visual regression testing in CI?

Yes. Capture screenshots on every pull request, compare them to baseline images, and fail the build if visual differences exceed a threshold.

How do I add screenshot evidence to pull requests?

Use the GitHub API to post a comment on the PR with embedded screenshot images. Upload the screenshots as artifacts or to an external host.

Can I capture screenshots of preview deployments?

Yes. Wait for your preview deployment to be ready, then pass its URL to ScreenshotAPI. This works with Vercel, Netlify, Cloudflare Pages, and any other preview environment.

How many credits do CI screenshots typically use?

It depends on your workflow. A typical setup capturing 5 pages on each PR uses 5 credits per run. With the Growth plan at 2,000 credits, that covers 400 PR builds.

Related resources

Start capturing screenshots today

Create a free account and get 5 credits to try the API. No credit card required. Pay only for what you use.