Tutorial

Migrate from Puppeteer to a Screenshot API: Step-by-Step

📦 Migration Guide

Complete Migration Guide: Puppeteer to SnapAPI

Replace 200+ lines of Puppeteer boilerplate with a single API call. This guide covers every common Puppeteer operation with side-by-side code comparisons, a migration checklist, and a real cost analysis.

📅 February 19, 2026 ⏱️ 15 min read 🏷️ Migration, Puppeteer, Developer Guide

1. Why Migrate Away from Puppeteer?

Puppeteer is powerful, but running it in production comes with significant operational overhead:

0
Servers to maintain
~80%
Less code to write
99.9%
API uptime SLA

The hidden costs of self-hosted Puppeteer:

  • Chrome/Chromium memory leaks — requires periodic restarts and monitoring
  • Browser crashes on complex pages — needs retry logic and error handling
  • Scaling headless browsers is hard — each instance uses 200-500MB RAM
  • Keeping Chrome up to date for security patches
  • Handling timeouts, zombie processes, and disk space for temp files
  • No built-in ad blocking, cookie banner removal, or device emulation presets

2. Side-by-Side Code Comparisons

Every common Puppeteer operation translated to SnapAPI. Notice the difference in complexity.

Basic Screenshot

❌ Puppeteer (22 lines)
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    headless: 'new',
    args: [
      '--no-sandbox',
      '--disable-setuid-sandbox',
      '--disable-dev-shm-usage'
    ]
  });
  const page = await browser.newPage();
  await page.setViewport({
    width: 1280,
    height: 800
  });
  await page.goto('https://example.com', {
    waitUntil: 'networkidle0'
  });
  await page.screenshot({
    path: 'screenshot.png'
  });
  await browser.close();
})();
✅ SnapAPI (8 lines)
const res = await fetch(
  'https://api.snapapi.pics/v1/screenshot',
  {
    method: 'POST',
    headers: {
      'x-api-key': 'YOUR_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      url: 'https://example.com'
    })
  }
);

fs.writeFileSync(
  'screenshot.png',
  Buffer.from(await res.arrayBuffer())
);

Full-Page Screenshot with Dark Mode

❌ Puppeteer (28 lines)
const browser = await puppeteer.launch({
  headless: 'new',
  args: ['--no-sandbox']
});
const page = await browser.newPage();
await page.setViewport({
  width: 1280, height: 800
});

// Force dark mode
await page.emulateMediaFeatures([
  {
    name: 'prefers-color-scheme',
    value: 'dark'
  }
]);

await page.goto('https://example.com', {
  waitUntil: 'networkidle0',
  timeout: 30000
});

await page.screenshot({
  path: 'full.png',
  fullPage: true
});
await browser.close();
✅ SnapAPI (1 API call)
const res = await fetch(
  'https://api.snapapi.pics/v1/screenshot',
  {
    method: 'POST',
    headers: {
      'x-api-key': 'YOUR_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      url: 'https://example.com',
      fullPage: true,
      darkMode: true
    })
  }
);

// That's it. No browser management,
// no Chrome flags, no cleanup.

Mobile Device Emulation

❌ Puppeteer
const iPhone = puppeteer.KnownDevices[
  'iPhone 15 Pro'
];
// Hope this device exists...
// Puppeteer's list is often outdated

const page = await browser.newPage();
await page.emulate(iPhone);
await page.goto('https://example.com');
await page.screenshot({
  path: 'mobile.png'
});
await browser.close();
✅ SnapAPI
// 25 real devices always up to date
{
  "url": "https://example.com",
  "device": "iphone-15-pro"
}

// Also: iphone-15-pro-max, ipad-pro-11,
// pixel-8-pro, samsung-galaxy-s24,
// macbook-pro-16, and more.

PDF Generation

❌ Puppeteer
const page = await browser.newPage();
await page.goto('https://example.com', {
  waitUntil: 'networkidle0'
});
await page.pdf({
  path: 'output.pdf',
  format: 'A4',
  printBackground: true,
  margin: {
    top: '1cm',
    bottom: '1cm',
    left: '1cm',
    right: '1cm'
  }
});
await browser.close();
✅ SnapAPI
{
  "url": "https://example.com",
  "pdfOptions": {
    "pageSize": "a4",
    "printBackground": true,
    "marginTop": "1cm",
    "marginBottom": "1cm"
  }
}

// POST to /v1/pdf
// Returns PDF binary directly

Element Screenshot with Wait

❌ Puppeteer
const page = await browser.newPage();
await page.goto('https://example.com');
await page.waitForSelector(
  '#chart-container',
  { timeout: 10000 }
);
// Extra delay for animations
await new Promise(
  r => setTimeout(r, 2000)
);
const el = await page.$(
  '#chart-container'
);
await el.screenshot({
  path: 'chart.png'
});
await browser.close();
✅ SnapAPI
{
  "url": "https://example.com",
  "selector": "#chart-container",
  "waitForSelector": "#chart-container",
  "delay": 2000
}

// All in a single parameter.
// No browser lifecycle management.
❌ Puppeteer (needs puppeteer-extra)
const puppeteer = require(
  'puppeteer-extra'
);
const AdblockerPlugin = require(
  'puppeteer-extra-plugin-adblocker'
);
puppeteer.use(AdblockerPlugin({
  blockTrackers: true
}));

// Cookie banners? You're on your own.
// Write custom CSS or JS to hide them.
// Or use yet another plugin.

const browser = await puppeteer.launch();
// ...
✅ SnapAPI
{
  "url": "https://example.com",
  "blockAds": true,
  "blockTrackers": true,
  "blockCookieBanners": true,
  "blockChatWidgets": true
}

// Built-in. No plugins needed.
// Always up to date filter lists.

Batch Processing

❌ Puppeteer (you build it yourself)
const urls = [/* 100 URLs */];
const CONCURRENCY = 5;

async function processUrl(url, browser) {
  const page = await browser.newPage();
  try {
    await page.goto(url, {
      timeout: 30000
    });
    await page.screenshot({/*...*/});
  } catch (e) {
    console.error(url, e);
  } finally {
    await page.close();
  }
}

// Build your own queue, concurrency
// limiter, retry logic, error handling
// ... 50+ more lines
✅ SnapAPI (built-in)
{
  "urls": [
    "https://example.com",
    "https://google.com",
    // ... up to 100 URLs
  ],
  "format": "png",
  "webhookUrl": "https://you.com/done"
}

// POST to /v1/screenshot/batch
// We handle concurrency, retries,
// and notify you when done.

3. Performance Comparison

1.2s
SnapAPI avg response
3.5s
Puppeteer avg (cold)
3x
Faster on average
MetricPuppeteer (self-hosted)SnapAPI
Cold start time2-5s (browser launch)0ms (always warm)
Avg screenshot time3-5s1-2s
Memory per instance200-500MB0MB (API call)
Max concurrent5-10 per serverUnlimited (rate limited)
Reliability95-98% (crashes)99.9% SLA
Chrome updatesManualAutomatic
💡 Pro tip

SnapAPI keeps browser pools warm 24/7. Your first request is as fast as your 1000th — no cold starts, no browser launch overhead.

4. Cost Comparison

Let's compare the real cost of running Puppeteer yourself vs. using SnapAPI for 50,000 screenshots/month:

Cost ItemSelf-Hosted PuppeteerSnapAPI
Server (4 CPU, 8GB RAM)$40-80/mo
DevOps / Monitoring$200-500/mo (time cost)
Error handling / retries$100-200/mo (dev time)
Chrome license / updatesFree but time-consuming
SnapAPI subscription$19/mo (Starter)
Total$340-780/mo$19/mo
⚠️ Hidden costs people forget

Developer time is the biggest cost. Debugging Puppeteer memory leaks at 3 AM, handling Chrome version mismatches, and building retry logic costs far more than the server itself.

5. Migration Checklist

🔄 Step-by-Step Migration

Sign up for SnapAPI — Get your API key from snapapi.pics/dashboard.html. Free tier includes 200 screenshots.
Map your Puppeteer operations — List every place in your code that uses Puppeteer. Most map directly to SnapAPI parameters.
Replace browser launch/close — Remove all puppeteer.launch() and browser.close(). Replace with HTTP fetch calls.
Map viewport settings — Convert page.setViewport() to width/height params, or use device presets.
Replace page.goto() — The URL goes into the request body's url field. waitUntil maps directly.
Replace page.screenshot() — Options like fullPage, format, quality map 1:1 to SnapAPI params.
Replace page.pdf() — Use the /v1/pdf endpoint. PDF options map directly.
Replace page.evaluate() — Use the javascript parameter to inject custom JS before capture.
Replace waitForSelector — Use the waitForSelector parameter. Same CSS selectors work.
Remove Puppeteer dependenciesnpm uninstall puppeteer puppeteer-extra puppeteer-extra-plugin-adblocker
Remove Chrome from Docker/server — No more 400MB+ Chrome binaries in your Docker images.
Test edge cases — Test with your most complex pages. SnapAPI handles SPAs, lazy-loaded content, and dynamic pages.
Set up error handling — Add retry logic for 429 (rate limit) responses. Check X-RateLimit-Remaining header.

6. Advanced Scenarios

Custom JavaScript Injection

If you use page.evaluate() to modify the page before capturing, use the javascript parameter:

SnapAPI — Custom JS injection
{
  "url": "https://example.com",
  "javascript": "document.querySelector('.banner').remove(); document.body.style.background = '#fff';",
  "delay": 500
}

Authentication / Cookies

If your Puppeteer code sets cookies for authenticated sessions:

SnapAPI — Authenticated screenshots
{
  "url": "https://app.example.com/dashboard",
  "cookies": [
    { "name": "session", "value": "abc123", "domain": "app.example.com" }
  ],
  "extraHeaders": {
    "Authorization": "Bearer your-token"
  }
}

Proxy & Geolocation

SnapAPI — Geo-targeted screenshot
{
  "url": "https://example.com",
  "proxy": { "server": "http://proxy.example.com:8080" },
  "geolocation": { "latitude": 40.7128, "longitude": -74.0060 },
  "timezone": "America/New_York",
  "locale": "en-US"
}

7. FAQ

Can I still use Puppeteer for some things?

Absolutely. SnapAPI covers screenshots, PDFs, videos, and content extraction. If you need complex multi-step browser automation (filling forms, clicking through flows), Puppeteer or Playwright may still be the right tool. Many teams use SnapAPI for capture tasks and keep a small Puppeteer setup for automation.

What about Playwright?

Everything in this guide applies to Playwright too. The API call is identical — just replace your Playwright code the same way. See our Playwright vs Puppeteer comparison.

Is there a Node.js SDK?

Yes! We have SDKs for JavaScript, Python, Go, PHP, Kotlin, and Swift. Check the documentation for installation instructions. But even without an SDK, it's just a fetch call — no SDK required.

What if I need more than 50,000 screenshots/month?

Our Business plan supports up to 500,000/month, and we offer custom Enterprise plans for higher volumes. Contact us for details.

Ready to Ditch Puppeteer?

Start with 200 free screenshots. No credit card required. Most teams migrate in under an hour.

Start Free → snapapi.pics

Start Capturing for Free

200 screenshots/month. Screenshots, PDF, scraping, and video recording. No credit card required.

Get Free API Key →