Screenshot API for Bitbucket Pipelines
Automate website screenshots in Bitbucket Pipelines with ScreenshotAPI. Visual testing, deploy evidence, and CI screenshot workflows.
Last updated: 2026-03-25
Try ScreenshotAPI free
5 free credits. No credit card required.
Automate Screenshots in Bitbucket Pipelines with ScreenshotAPI
Bitbucket Pipelines provides CI/CD directly within Bitbucket Cloud. Adding screenshot capability to your pipelines is valuable for visual regression testing, deploy verification, and creating visual change logs. Installing Puppeteer on Bitbucket Pipelines requires a Docker image with Chromium and its dependencies, which increases build times and adds maintenance burden.
ScreenshotAPI simplifies your Bitbucket Pipelines screenshot integration to simple HTTP requests. No browser binary, no specialized Docker images, no extra build minutes for Chromium setup.
Quick Start
- Sign up for ScreenshotAPI and copy your API key. 5 free credits are included.
- Add the key as a secured repository variable.
- Add screenshot steps to your
bitbucket-pipelines.yml.
Installation
Add the API key as a repository variable:
- Go to Repository settings > Pipelines > Repository variables.
- Set Name to
SCREENSHOTAPI_KEYand Value to your API key. - Check Secured to mask it in logs.
No additional Docker images or packages are needed. The alpine or node images with curl or node are sufficient.
Basic Pipeline: Screenshot on Deploy
Capture screenshots after deploying to production:
yaml# bitbucket-pipelines.yml pipelines: branches: main: - step: name: Deploy script: - echo "Deploying to production..." - step: name: Capture Screenshots image: alpine:latest script: - apk add --no-cache curl - mkdir -p screenshots - | for page in "" "/pricing" "/docs" "/blog"; do name=$(echo "$page" | sed 's/^\///' | sed 's/^$/homepage/') curl -s -o "screenshots/${name}.png" \ -H "x-api-key: ${SCREENSHOTAPI_KEY}" \ "https://screenshotapi.to/api/v1/screenshot?url=https://yoursite.com${page}&width=1440&height=900&type=png" echo "Captured ${name}.png" done artifacts: - screenshots/**
Visual Regression Testing on Pull Requests
Capture screenshots on every PR and compare to baselines:
yaml# bitbucket-pipelines.yml pipelines: pull-requests: '**': - step: name: Visual Regression Test image: node:20-alpine script: - node scripts/capture-screenshots.js - node scripts/compare-screenshots.js artifacts: - screenshots/**
Capture Script
javascript// 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' }, ]; const outputDir = 'screenshots/current'; fs.mkdirSync(outputDir, { recursive: true }); async function capture(page) { const url = `${BASE_URL}${page.path}`; const params = new URLSearchParams({ url, width: '1440', height: '900', type: 'png', waitUntil: 'networkidle', }); try { const response = await fetch(`${API_BASE}?${params}`, { headers: { 'x-api-key': API_KEY }, }); if (!response.ok) { console.error(`Failed: ${page.name} (${response.status})`); return; } const buffer = Buffer.from(await response.arrayBuffer()); fs.writeFileSync(path.join(outputDir, `${page.name}.png`), buffer); console.log(`Captured ${page.name} (${buffer.length} bytes)`); } catch (err) { console.error(`Error capturing ${page.name}: ${err.message}`); } } async function main() { await Promise.all(pages.map(capture)); } main();
Comparison Script
javascript// scripts/compare-screenshots.js const fs = require('node:fs'); const path = require('node:path'); const currentDir = 'screenshots/current'; const baselineDir = 'screenshots/baseline'; if (!fs.existsSync(baselineDir)) { console.log('No baseline found. Saving current as baseline.'); fs.cpSync(currentDir, baselineDir, { recursive: true }); process.exit(0); } const files = fs.readdirSync(currentDir).filter(f => f.endsWith('.png')); let hasChanges = false; for (const file of files) { const currentPath = path.join(currentDir, file); const baselinePath = path.join(baselineDir, file); if (!fs.existsSync(baselinePath)) { console.log(`New page: ${file}`); hasChanges = true; continue; } const current = fs.readFileSync(currentPath); const baseline = fs.readFileSync(baselinePath); if (!current.equals(baseline)) { console.log(`Changed: ${file}`); hasChanges = true; } else { console.log(`Unchanged: ${file}`); } } if (hasChanges) { console.log('\nVisual changes detected. Review the artifacts.'); process.exit(1); }
Multi-Page with Custom Script
Capture multiple pages and viewports using a Node.js script:
yamlpipelines: custom: multi-viewport-screenshots: - step: name: Multi-Viewport Capture image: node:20-alpine script: - node scripts/multi-viewport.js artifacts: - screenshots/**
javascript// scripts/multi-viewport.js const fs = require('node:fs'); const API_BASE = 'https://screenshotapi.to/api/v1/screenshot'; const API_KEY = process.env.SCREENSHOTAPI_KEY; const viewports = [ { name: 'desktop', width: 1440, height: 900 }, { name: 'tablet', width: 768, height: 1024 }, { name: 'mobile', width: 390, height: 844 }, ]; const pages = ['https://yoursite.com', 'https://yoursite.com/pricing']; fs.mkdirSync('screenshots', { recursive: true }); async function captureAll() { for (const page of pages) { for (const vp of viewports) { const pageName = new URL(page).pathname.replace(/\//g, '-') || 'home'; const filename = `screenshots/${pageName}-${vp.name}.png`; const params = new URLSearchParams({ url: page, width: String(vp.width), height: String(vp.height), type: 'png', }); const response = await fetch(`${API_BASE}?${params}`, { headers: { 'x-api-key': API_KEY }, }); if (response.ok) { const buffer = Buffer.from(await response.arrayBuffer()); fs.writeFileSync(filename, buffer); console.log(`${filename} (${buffer.length} bytes)`); } else { console.error(`Failed: ${filename} (${response.status})`); } } } } captureAll();
Scheduled Monitoring
Run screenshots on a schedule using Bitbucket's scheduled pipelines:
yamlpipelines: custom: site-monitor: - step: name: Monitor Screenshots image: alpine:latest script: - apk add --no-cache curl - mkdir -p screenshots - TIMESTAMP=$(date +%Y%m%d-%H%M) - | curl -s -o "screenshots/homepage-${TIMESTAMP}.png" \ -H "x-api-key: ${SCREENSHOTAPI_KEY}" \ "https://screenshotapi.to/api/v1/screenshot?url=https://yoursite.com&width=1440&height=900&type=png" curl -s -o "screenshots/pricing-${TIMESTAMP}.png" \ -H "x-api-key: ${SCREENSHOTAPI_KEY}" \ "https://screenshotapi.to/api/v1/screenshot?url=https://yoursite.com/pricing&width=1440&height=900&type=png" artifacts: - screenshots/**
Configure the schedule in Pipelines > Schedules. Set it to run the site-monitor custom pipeline at your preferred interval.
Parallel Steps
Capture multiple pages in parallel using Bitbucket's parallel step feature:
yamlpipelines: branches: main: - parallel: - step: name: Screenshot Homepage image: alpine:latest script: - apk add --no-cache curl - curl -s -o homepage.png -H "x-api-key: ${SCREENSHOTAPI_KEY}" "https://screenshotapi.to/api/v1/screenshot?url=https://yoursite.com&width=1440&height=900&type=png" artifacts: - homepage.png - step: name: Screenshot Pricing image: alpine:latest script: - apk add --no-cache curl - curl -s -o pricing.png -H "x-api-key: ${SCREENSHOTAPI_KEY}" "https://screenshotapi.to/api/v1/screenshot?url=https://yoursite.com/pricing&width=1440&height=900&type=png" artifacts: - pricing.png
Production Tips
Build Minutes
Bitbucket Pipelines charges by build minutes. ScreenshotAPI keeps steps fast because there is no browser to install or start. A screenshot step typically completes in 5-15 seconds.
Artifacts
Download artifacts from the Pipelines tab. Set retention appropriate to your needs. Visual regression baselines should be committed to the repository for persistence across builds.
Repository Variables
Use secured repository variables for the API key. For organization-wide access, use workspace variables instead of per-repository settings.
Credit Planning
Each screenshot uses one credit. A pipeline capturing 5 pages on 60 PRs per month uses 300 credits. Visit the pricing page to find the right tier.
Further Reading
- The GitHub Actions integration covers GitHub's CI/CD platform.
- See the GitLab CI integration for GitLab pipeline patterns.
- Learn about visual regression testing for a comprehensive testing strategy.
Frequently asked questions
Do I need Chrome installed on Bitbucket Pipelines?
No. ScreenshotAPI handles browser rendering remotely. Your pipeline step uses curl or a lightweight script to make HTTP requests. No Chromium installation needed.
How do I store the API key in Bitbucket Pipelines?
Add it as a repository variable in Settings > Repository variables. Mark it as Secured so it is masked in pipeline logs.
Can I capture screenshots of Bitbucket preview environments?
Yes. Pass the preview deployment URL to ScreenshotAPI in your pipeline step. This works with any hosting provider that creates preview URLs for branches or pull requests.
How do I download screenshots from a Bitbucket Pipeline?
Configure the step with an artifacts section. After the pipeline completes, download the screenshots from the Artifacts tab in the pipeline results page.
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.