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.
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
- Sign up for ScreenshotAPI and copy your API key. 5 free credits are included.
- Add the API key as a GitHub Actions secret.
- Create a workflow that captures screenshots.
Installation
Add your API key as a repository secret:
- Go to your repository Settings > Secrets and variables > Actions.
- Click "New repository secret".
- Name it
SCREENSHOTAPI_KEYand 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:
yamlname: 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
- Learn about visual regression testing for a comprehensive testing strategy.
- See the Vercel integration for combining preview deployments with screenshots.
- Read How to Take Screenshots with JavaScript for the fundamentals.
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.