Guide Mar 28, 2026

Take Screenshots Programmatically in Node.js: One HTTP Call, No Puppeteer

Capture screenshots without installing Chromium or managing browser lifecycle. One fetch() call returns a PNG in under a second — batch, parallel, CI/CD-ready.

You need to take a screenshot in your Node.js app. The traditional approach: install Puppeteer, manage Chromium, write boilerplate code, handle browser lifecycle. It's slow and heavy.

PageBolt's /screenshot endpoint solves this. One HTTP call, one screenshot. No browser to manage, no dependencies beyond the built-in fetch API.

Why This Matters

Every Node.js developer eventually hits this need: screenshots for error reports, audit trails, thumbnail previews, or visual testing. With Puppeteer, you get:

With a screenshot API: one HTTP request, 500–800ms response, no dependencies, no memory overhead.

Basic Screenshot: One HTTP Call

const response = await fetch('https://pagebolt.dev/api/v1/screenshot', {
  method: 'POST',
  headers: {
    'x-api-key': process.env.PAGEBOLT_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://example.com/dashboard',
    width: 1440,
    height: 900,
    format: 'png',
  }),
});

const imageBuffer = await response.arrayBuffer();
console.log(`Screenshot captured: ${imageBuffer.byteLength} bytes`);

The image is in memory, ready to save or send.

Save to Disk

import fs from 'fs';

const response = await fetch('https://pagebolt.dev/api/v1/screenshot', {
  method: 'POST',
  headers: {
    'x-api-key': process.env.PAGEBOLT_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ url: 'https://example.com/dashboard', format: 'png' }),
});

const buffer = await response.arrayBuffer();
fs.writeFileSync('screenshot.png', Buffer.from(buffer));
console.log('Screenshot saved to screenshot.png');

Batch Screenshots (Sequential)

async function captureScreenshots(urls) {
  const results = [];

  for (const url of urls) {
    try {
      const response = await fetch('https://pagebolt.dev/api/v1/screenshot', {
        method: 'POST',
        headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
        body: JSON.stringify({ url, format: 'webp', quality: 80 }),
      });

      if (!response.ok) throw new Error(`API error: ${response.status}`);

      const buffer = await response.arrayBuffer();
      results.push({ url, size: buffer.byteLength, data: Buffer.from(buffer) });
      console.log(`✓ ${url} (${buffer.byteLength} bytes)`);
    } catch (error) {
      console.error(`✗ ${url} failed: ${error.message}`);
      results.push({ url, error: error.message });
    }
  }

  return results;
}

const screenshots = await captureScreenshots([
  'https://example.com/page1',
  'https://example.com/page2',
  'https://example.com/page3',
]);

Each screenshot takes ~500–800ms. 10 screenshots ≈ 5–8 seconds total, with clean error handling.

Parallel Screenshots: Promise.all()

For faster batch processing, run all screenshots concurrently:

async function captureParallel(urls) {
  const promises = urls.map(async (url) => {
    try {
      const response = await fetch('https://pagebolt.dev/api/v1/screenshot', {
        method: 'POST',
        headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
        body: JSON.stringify({ url, format: 'webp', quality: 85, fullPage: true }),
      });
      const buffer = await response.arrayBuffer();
      return { url, success: true, size: buffer.byteLength, data: Buffer.from(buffer) };
    } catch (error) {
      return { url, success: false, error: error.message };
    }
  });

  return Promise.all(promises);
}

const results = await captureParallel([
  'https://example.com/page1',
  'https://example.com/page2',
  'https://example.com/page3',
]);
console.log(`✓ ${results.filter(r => r.success).length}/${results.length} captured`);

All 10 screenshots run in parallel: ~800ms total instead of 8 seconds.

CI/CD Integration: GitHub Actions

name: Screenshot Tests
on: [push, pull_request]

jobs:
  screenshots:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Capture staging screenshot
        env:
          PAGEBOLT_API_KEY: ${{ secrets.PAGEBOLT_API_KEY }}
        run: |
          node -e "
            const fs = require('fs');
            fetch('https://pagebolt.dev/api/v1/screenshot', {
              method: 'POST',
              headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
              body: JSON.stringify({ url: 'https://staging.example.com/dashboard', format: 'png', width: 1440, height: 900 }),
            }).then(r => r.arrayBuffer()).then(buf => {
              fs.writeFileSync('screenshot.png', Buffer.from(buf));
              console.log('Screenshot captured');
            });
          "

      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: screenshots
          path: screenshot.png

Captures a screenshot on every PR, zero infrastructure overhead — no Chromium installation step needed.

Full-Page and Mobile Screenshots

// Full page — auto-scrolls to capture all content
const fullPage = await fetch('https://pagebolt.dev/api/v1/screenshot', {
  method: 'POST',
  headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    url: 'https://example.com/landing',
    fullPage: true,
    fullPageScroll: true,
    fullPageMaxHeight: 10000,
    format: 'png',
  }),
});

// iPhone 14 Pro emulation
const mobile = await fetch('https://pagebolt.dev/api/v1/screenshot', {
  method: 'POST',
  headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({
    url: 'https://example.com/landing',
    viewportDevice: 'iphone_14_pro',
    format: 'png',
  }),
});

Error Handling

async function takeScreenshot(url) {
  const response = await fetch('https://pagebolt.dev/api/v1/screenshot', {
    method: 'POST',
    headers: { 'x-api-key': process.env.PAGEBOLT_API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ url, format: 'png' }),
  });

  if (!response.ok) {
    if (response.status === 401) throw new Error('Invalid API key');
    if (response.status === 402) throw new Error('Monthly quota exceeded');
    if (response.status === 429) throw new Error('Rate limit exceeded — retry shortly');
    throw new Error(`API error: ${response.status}`);
  }

  return response.arrayBuffer();
}

Cost Comparison: API vs Puppeteer

MetricPuppeteerPageBolt API
1 screenshot~500ms cold start500–800ms
100 screenshots/mo$50–100 infra + dev time$1–5
1,000 screenshots/mo$200–500 + maintenance$10–20
Memory per screenshot200–500MB~1MB overhead
Setup time2+ hours + debugging5 minutes
Chromium managementManualManaged

Getting Started

  1. Sign upFree tier: 100 requests/month, no credit card
  2. Get your API key — Available immediately from the dashboard
  3. Test it — Copy the basic example above and run it locally
  4. Integrate — Use the async/await patterns in your app
  5. Scale — Monitor usage in the dashboard; upgrade if needed

Screenshots in Node.js don't require Puppeteer. Try PageBolt free — 100 screenshots/month, no credit card needed.

Add screenshots to your Node.js app — free

100 requests/month, no credit card. One fetch() call, no Puppeteer, no Chromium.

Get API key free →